Symfony 3, заполняющий токен и обновляющий пользователя


Репозиторий с проблемой

У меня есть форма для пользователя сущности с полем электронной почты:

->add('email', EmailType::class, [
                'constraints' => [
                    new NotBlank(),
                    new Email([
                        'checkMX' => true,
                    ])
                ],
                'required' => true
            ])

Когда я редактирую электронную почту на что-то вроде [email protected] и отправляю форму, она показывает мне ошибку "Это значение не является действительным адресом электронной почты". Это нормально, но после этого symfony заполняет неправильное электронное письмо в токен, и когда я перехожу на любую другую страницу или просто перезагружаю страницу, я получаю следующее:

ПРЕДУПРЕЖДЕНИЕ Имя пользователя безопасности не удалось найти в выбранном поставщик услуг пользователя.

Я думаю, вопрос в следующем: почему symfony заполняет неверное электронное письмо, которое не прошло проверку, в токен и как я могу это предотвратить?

Контроллер:

public function meSettingsAction(Request $request)
    {

        $user = $this->getUser();
        $userUnSubscribed = $this->getDoctrine()->getRepository('AppBundle:UserUnsubs')->findOneBy(
            [
                'email' => $user->getEmail(),
            ]
        );

        $form = $this->createForm(UserSettingsType::class, $user);
        $form->get('subscribed')->setData(!(bool)$userUnSubscribed);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /**
             * @var $user User
             */
            $user = $form->getData();

            /** @var UploadedFile $avatar */
            $avatar = $request->files->get('user_settings')['photo'];

            $em = $this->getDoctrine()->getManager();

            if ($avatar) {
                $avatar_content = file_get_contents($avatar->getRealPath());
                $avatarName = uniqid().'.jpg';
                $oldAvatar = $user->getPhoto();
                $user
                    ->setState(User::PHOTO_STATE_UNCHECKED)
                    ->setPhoto($avatarName);
                $gearmanClient = $this->get('gearman.client');
                $gearmanClient->doBackgroundDependsOnEnv(
                    'avatar_content_upload',
                    serialize(['content' => $avatar_content, 'avatarName' => $avatarName, 'oldAvatar' => $oldAvatar])
                );
            }

            $subscribed = $form->get('subscribed')->getData();
            if ((bool)$userUnSubscribed && $subscribed) {
                $em->remove($userUnSubscribed);
            } elseif (!(bool)$userUnSubscribed && !$subscribed) {
                $userUnSubscribed = new UserUnsubs();
                $userUnSubscribed->setEmail($form->get('email')->getData())->setTs(time());
                $em->persist($userUnSubscribed);
            }
            $user->setLastTs(time());
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            $this->get('user.manager')->refresh($user);

            return $this->redirectToRoute('me');
        }

        return $this->render(
            ':user:settings.html.twig',
            [
                'form' => $form->createView(),
            ]
        );
    }

UPD: все работает нормально, если я изменюсь в OAuthProvider:

/**
 * @param \Symfony\Component\Security\Core\User\UserInterface $user
 *
 * @return \Symfony\Component\Security\Core\User\UserInterface
 */
public function refreshUser(UserInterface $user)
{
    return $this->loadUserByUsername($user->getName());
}

Кому:

/**
 * @param \Symfony\Component\Security\Core\User\UserInterface $user
 *
 * @return \Symfony\Component\Security\Core\User\UserInterface
 */
public function refreshUser(UserInterface $user)
{
    return $this->userManager($user->getId());
}

Но, похоже, это грязный хак.

Спасибо.

Author: kRicha, 2017-06-08

2 answers

Это сложная задача, благодаря репозиторию было легче изолировать проблему. Вы привязываете объект пользователя из токена аутентификации к методу createForm(). После

$form->handleRequest($request)

Вызовите сообщение электронной почты, объект пользователя токена обновлен.

Я сначала подумал решить эту проблему, реализовав EquatableInterface.html в сущностиUser, но это не сработало, так как у сравниваемого объекта уже был неверный адрес электронной почты.

Это также может было бы полезно реализовать интерфейс EquatableInterface, который определяет метод проверки, равен ли пользователь текущему пользователю. Для этого интерфейса требуется метод isEqualTo().)

Чем я думал о принудительной перезагрузке пользователя из бд и сбросе маркера безопасности, но мне пришло в голову, что может быть достаточно просто обновить текущий объект пользователя из базы данных в случае сбоя формы:

$this->get('doctrine')->getManager()->refresh($this->getUser());`

В вашем контроллере это решило бы вашу вопрос.

/**
 * @Route("/edit_me", name="edit")
 * @Security("has_role('ROLE_USER')")
 */
public function editMyselfAction(Request $request) {
    $form = $this->createForm(User::class, $this->getUser());

    if ($request->isMethod(Request::METHOD_POST)) {
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();
        } else {
            $this->get('doctrine')->getManager()->refresh($this->getUser());
        }
    }

    return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}

Альтернативное решение

Проблема в репозитории Symfony привела к некоторым ценным сведениям о Избегании сущностей в формах и Разъединение вашего пользователя безопасности, что обеспечивает более сложный подход к решению вашей проблемы.

 1
Author: lordrhodos, 2017-06-17 22:26:30

Ваш пользовательский токен, похоже, обновляется формой, даже если ограничение электронной почты останавливает сброс.

Можете ли вы проверить, прошла ли ваша форма мимо функции isValid? Возможно, вы можете попытаться избежать этого с помощью прослушивателя событий или валидатора.

При отправке события вы должны иметь возможность проверить целостность электронной почты, а затем добавить ошибку, чтобы избежать пользователя обновления.

 3
Author: fireaxe, 2017-06-13 12:49:33