src/Controller/Shop/UserController.php line 275

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Sylius package.
  4.  *
  5.  * (c) Paweł Jędrzejewski
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace App\Controller\Shop;
  12. use App\Entity\User\ShopUser;
  13. use App\Security\Guard\ShopUserProxyAuthenticator;
  14. use App\Service\ApiBridge;
  15. use Doctrine\ORM\EntityManagerInterface;
  16. use FOS\RestBundle\View\View;
  17. use Psr\Container\ContainerExceptionInterface;
  18. use Psr\Container\NotFoundExceptionInterface;
  19. use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
  20. use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
  21. use Sylius\Bundle\UserBundle\Form\Model\ChangePassword;
  22. use Sylius\Bundle\UserBundle\Form\Model\PasswordResetRequest;
  23. use Sylius\Bundle\UserBundle\Form\Type\UserChangePasswordType;
  24. use Sylius\Bundle\UserBundle\Form\Type\UserRequestPasswordResetType;
  25. use Sylius\Bundle\UserBundle\Form\Type\UserResetPasswordType;
  26. use Sylius\Bundle\UserBundle\UserEvents;
  27. use Sylius\Component\User\Model\UserInterface;
  28. use Sylius\Component\User\Repository\UserRepositoryInterface;
  29. use Sylius\Component\User\Security\Generator\GeneratorInterface;
  30. use Symfony\Component\EventDispatcher\GenericEvent;
  31. use Symfony\Component\Form\FormInterface;
  32. use Symfony\Component\HttpFoundation\RedirectResponse;
  33. use Symfony\Component\HttpFoundation\Request;
  34. use Symfony\Component\HttpFoundation\Response;
  35. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  36. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  37. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  38. use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
  39. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  40. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  41. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  42. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  43. use Webmozart\Assert\Assert;
  44. class UserController extends ResourceController
  45. {
  46.     public function changePasswordAction(Request $request): Response
  47.     {
  48.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  49.         if (!$this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
  50.             throw new AccessDeniedException('You have to be registered user to access this section.');
  51.         }
  52.         $user $this->container->get('security.token_storage')->getToken()->getUser();
  53.         $changePassword = new ChangePassword();
  54.         $formType $this->getSyliusAttribute($request'form'UserChangePasswordType::class);
  55.         $form $this->createResourceForm($configuration$formType$changePassword);
  56.         if (in_array($request->getMethod(), ['POST''PUT''PATCH'], true) && $form->handleRequest($request)->isValid()) {
  57.             return $this->handleChangePassword($request$configuration$user$changePassword->getNewPassword());
  58.         }
  59.         if (!$configuration->isHtmlRequest()) {
  60.             return $this->viewHandler->handle($configurationView::create($formResponse::HTTP_BAD_REQUEST));
  61.         }
  62.         return new Response($this->container->get('twig')->render(
  63.             $configuration->getTemplate('changePassword.html'),
  64.             ['form' => $form->createView()]
  65.         ));
  66.     }
  67.     public function requestPasswordResetTokenAction(Request $requestApiBridge $api): Response
  68.     {
  69.         /** @var GeneratorInterface $generator */
  70.         $generator $this->container->get(sprintf('sylius.%s.token_generator.password_reset'$this->metadata->getName()));
  71.         return $this->prepareResetPasswordRequest(
  72.             $request,
  73.             $api,
  74.             UserEvents::REQUEST_RESET_PASSWORD_TOKEN
  75.         );
  76.     }
  77.     public function requestPasswordResetPinAction(Request $request): Response
  78.     {
  79.         /** @var GeneratorInterface $generator */
  80.         $generator $this->container->get(sprintf('sylius.%s.pin_generator.password_reset'$this->metadata->getName()));
  81.         return $this->prepareResetPasswordRequest($request$generatorUserEvents::REQUEST_RESET_PASSWORD_PIN);
  82.     }
  83.     /**
  84.      * @param Request $request
  85.      * @param ApiBridge $apiBridge
  86.      * @param string $token
  87.      * @param EntityManagerInterface $em
  88.      * @param GuardAuthenticatorHandler $guardAuthenticatorHandler
  89.      * @param ShopUserProxyAuthenticator $shopUserProxyAuthenticator
  90.      * @return Response
  91.      * @throws ClientExceptionInterface
  92.      * @throws ContainerExceptionInterface
  93.      * @throws NotFoundExceptionInterface
  94.      * @throws RedirectionExceptionInterface
  95.      * @throws ServerExceptionInterface
  96.      * @throws TransportExceptionInterface
  97.      * @throws \JsonException
  98.      */
  99.     public function resetPasswordAction(
  100.         Request $request,
  101.         ApiBridge $apiBridge,
  102.         string $token,
  103.         EntityManagerInterface $em,
  104.         GuardAuthenticatorHandler $guardAuthenticatorHandler,
  105.         ShopUserProxyAuthenticator $shopUserProxyAuthenticator
  106.     ): Response {
  107.         if (!$account $apiBridge->findCrmAccount($token)) {
  108.             throw new NotFoundHttpException('Account not found');
  109.         }
  110.         $form $this->createForm(UserResetPasswordType::class);
  111.         $form->handleRequest($request);
  112.         /** @var ShopUser */
  113.         $shopUser $em->getRepository(ShopUser::class)->findOneBy(['username' => $account['email']]);
  114.         if (!$shopUser) {
  115.             throw new NotFoundHttpException('Account not found');
  116.         }
  117.         $shopUser->setPasswordResetToken($token);
  118.         if ($form->isSubmitted()) {
  119.             if ($form->isValid()) {
  120.                 $data = [
  121.                     'id' => $account['id'],
  122.                     'password' => $form->get('password')->getData(), // TODO FIXME ne pas envoyer le pswd en clair d'une api à l'autre
  123.                     'enabled' => true,
  124.                     'activationToken' => null
  125.                 ];
  126.                 try {
  127.                     $apiBridge->updateCrmAccount($data);
  128.                 } catch (\Exception|TransportExceptionInterface $e) {
  129.                     $this->addTranslatedFlash('error''Le nouveau mot de passe est identique au précédent');
  130.                     return $this->redirectToRoute('sylius_shop_password_reset', ['token' => $token]);
  131.                 }
  132.                 /** @var ShopUser $shopUser */
  133.                 $shopUser->setEnabled(true);
  134.                 $shopUser->setEmailVerificationToken(null);
  135.                 $shopUser->setVerifiedAt(new \DateTime('now'));
  136.                 $em->flush();
  137.                 $this->addTranslatedFlash('success''sylius.user.reset_password');
  138.                 // Auto-login ShopUser & return to Homepage
  139.                 return $guardAuthenticatorHandler->authenticateUserAndHandleSuccess(
  140.                     $shopUser,
  141.                     $request,
  142.                     $shopUserProxyAuthenticator,
  143.                     'shop'
  144.                 );
  145.             }
  146.         }
  147.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  148.         return new Response($this->container->get('twig')->render(
  149.             $configuration->getTemplate('resetPassword.html'),
  150.             [
  151.                 'form' => $form->createView(),
  152.                 'user' => $shopUser,
  153.             ]
  154.         ));
  155.     }
  156.     public function verifyAction(Request $requeststring $token): Response
  157.     {
  158.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  159.         $redirectRoute $this->getSyliusAttribute($request'redirect'null);
  160.         $response $this->redirectToRoute($redirectRoute);
  161.         /** @var UserInterface|null $user */
  162.         $user $this->repository->findOneBy(['emailVerificationToken' => $token]);
  163.         if (null === $user) {
  164.             if (!$configuration->isHtmlRequest()) {
  165.                 return $this->viewHandler->handle($configurationView::create($configurationResponse::HTTP_BAD_REQUEST));
  166.             }
  167.             $this->addTranslatedFlash('error''sylius.user.verify_email_by_invalid_token');
  168.             return $this->redirectToRoute($redirectRoute);
  169.         }
  170.         $user->setVerifiedAt(new \DateTime());
  171.         $user->setEmailVerificationToken(null);
  172.         $user->enable();
  173.         $eventDispatcher $this->container->get('event_dispatcher');
  174.         $eventDispatcher->dispatch(new GenericEvent($user), UserEvents::PRE_EMAIL_VERIFICATION);
  175.         $this->manager->flush();
  176.         $eventDispatcher->dispatch(new GenericEvent($user), UserEvents::POST_EMAIL_VERIFICATION);
  177.         if (!$configuration->isHtmlRequest()) {
  178.             return $this->viewHandler->handle($configurationView::create($user));
  179.         }
  180.         $flashMessage $this->getSyliusAttribute($request'flash''sylius.user.verify_email');
  181.         $this->addTranslatedFlash('success'$flashMessage);
  182.         return $response;
  183.     }
  184.     public function requestVerificationTokenAction(Request $request): Response
  185.     {
  186.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  187.         $redirectRoute $this->getSyliusAttribute($request'redirect''referer');
  188.         $user $this->getUser();
  189.         if (null === $user) {
  190.             if (!$configuration->isHtmlRequest()) {
  191.                 return $this->viewHandler->handle($configurationView::create($configurationResponse::HTTP_UNAUTHORIZED));
  192.             }
  193.             $this->addTranslatedFlash('notice''sylius.user.verify_no_user');
  194.             return $this->redirectHandler->redirectToRoute($configuration$redirectRoute);
  195.         }
  196.         if (null !== $user->getVerifiedAt()) {
  197.             if (!$configuration->isHtmlRequest()) {
  198.                 return $this->viewHandler->handle($configurationView::create($configurationResponse::HTTP_BAD_REQUEST));
  199.             }
  200.             $this->addTranslatedFlash('notice''sylius.user.verify_verified_email');
  201.             return $this->redirectHandler->redirectToRoute($configuration$redirectRoute);
  202.         }
  203.         /** @var GeneratorInterface $tokenGenerator */
  204.         $tokenGenerator $this->container->get(sprintf('sylius.%s.token_generator.email_verification'$this->metadata->getName()));
  205.         $user->setEmailVerificationToken($tokenGenerator->generate());
  206.         $this->manager->flush();
  207.         $eventDispatcher $this->container->get('event_dispatcher');
  208.         $eventDispatcher->dispatch(new GenericEvent($user), UserEvents::REQUEST_VERIFICATION_TOKEN);
  209.         if (!$configuration->isHtmlRequest()) {
  210.             return $this->viewHandler->handle($configurationView::create(nullResponse::HTTP_NO_CONTENT));
  211.         }
  212.         $this->addTranslatedFlash('success''sylius.user.verify_email_request');
  213.         return $this->redirectHandler->redirectToRoute($configuration$redirectRoute);
  214.     }
  215.     protected function prepareResetPasswordRequest(Request $requestApiBridge $apistring $senderEvent): Response
  216.     {
  217.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  218.         $passwordReset = new PasswordResetRequest();
  219.         $formType $this->getSyliusAttribute($request'form'UserRequestPasswordResetType::class);
  220.         $form $this->createResourceForm($configuration$formType$passwordReset);
  221.         $template $this->getSyliusAttribute($request'template'null);
  222.         if ($configuration->isHtmlRequest()) {
  223.             Assert::notNull($template'Template is not configured.');
  224.         }
  225.         if (in_array($request->getMethod(), ['POST''PUT''PATCH'], true) && $form->handleRequest($request)->isValid()) {
  226.             $userRepository $this->repository;
  227.             /** @var UserRepositoryInterface $userRepository */
  228.             Assert::isInstanceOf($userRepositoryUserRepositoryInterface::class);
  229.             $user $userRepository->findOneByEmail($passwordReset->getEmail());
  230.             if (null !== $user) {
  231.                 $this->handleResetPasswordRequest($api$user$senderEvent);
  232.             }
  233.             if (!$configuration->isHtmlRequest()) {
  234.                 return $this->viewHandler->handle($configurationView::create(nullResponse::HTTP_NO_CONTENT));
  235.             }
  236.             $this->addTranslatedFlash('success''sylius.user.reset_password_request');
  237.             $redirectRoute $this->getSyliusAttribute($request'redirect'null);
  238.             Assert::notNull($redirectRoute'Redirect is not configured.');
  239.             if (is_array($redirectRoute)) {
  240.                 return $this->redirectHandler->redirectToRoute(
  241.                     $configuration,
  242.                     $configuration->getParameters()->get('redirect')['route'],
  243.                     $configuration->getParameters()->get('redirect')['parameters']
  244.                 );
  245.             }
  246.             return $this->redirectHandler->redirectToRoute($configuration$redirectRoute);
  247.         }
  248.         if (!$configuration->isHtmlRequest()) {
  249.             return $this->viewHandler->handle($configurationView::create($formResponse::HTTP_BAD_REQUEST));
  250.         }
  251.         return new Response($this->container->get('twig')->render(
  252.             $template,
  253.             [
  254.                 'form' => $form->createView(),
  255.             ]
  256.         ));
  257.     }
  258.     protected function addTranslatedFlash(string $typestring $message): void
  259.     {
  260.         $translator $this->container->get('translator');
  261.         $this->container->get('session')->getFlashBag()->add($type$translator->trans($message, [], 'flashes'));
  262.     }
  263.     /**
  264.      * @param object $object
  265.      */
  266.     protected function createResourceForm(
  267.         RequestConfiguration $configuration,
  268.         string $type,
  269.         $object
  270.     ): FormInterface {
  271.         if (!$configuration->isHtmlRequest()) {
  272.             return $this->container->get('form.factory')->createNamed(''$type$object, ['csrf_protection' => false]);
  273.         }
  274.         return $this->container->get('form.factory')->create($type$object);
  275.     }
  276.     protected function handleExpiredToken(Request $requestRequestConfiguration $configurationUserInterface $user): Response
  277.     {
  278.         $user->setPasswordResetToken(null);
  279.         $user->setPasswordRequestedAt(null);
  280.         $this->manager->flush();
  281.         if (!$configuration->isHtmlRequest()) {
  282.             return $this->viewHandler->handle($configurationView::create($userResponse::HTTP_BAD_REQUEST));
  283.         }
  284.         $this->addTranslatedFlash('error''sylius.user.expire_password_reset_token');
  285.         $redirectRouteName $this->getSyliusAttribute($request'redirect'null);
  286.         Assert::notNull($redirectRouteName'Redirect is not configured.');
  287.         return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
  288.     }
  289.     protected function handleResetPasswordRequest(
  290.         ApiBridge $api,
  291.         UserInterface $user
  292.     ): void {
  293.         $redirectUrl $this->generateUrl(
  294.             'sylius_shop_request_password_reset_token',
  295.             [],
  296.             UrlGeneratorInterface::ABSOLUTE_URL
  297.         );
  298.         $api->requestCrmResetPassword($user->getEmail(), $redirectUrl '/');
  299.     }
  300.     protected function handleResetPassword(
  301.         Request $request,
  302.         RequestConfiguration $configuration,
  303.         UserInterface $user,
  304.         string $newPassword
  305.     ): Response {
  306.         $user->setPlainPassword($newPassword);
  307.         $user->setPasswordResetToken(null);
  308.         $user->setPasswordRequestedAt(null);
  309.         $dispatcher $this->container->get('event_dispatcher');
  310.         $dispatcher->dispatch(new GenericEvent($user), UserEvents::PRE_PASSWORD_RESET);
  311.         $this->manager->flush();
  312.         $this->addTranslatedFlash('success''sylius.user.reset_password');
  313.         $dispatcher->dispatch(new GenericEvent($user), UserEvents::POST_PASSWORD_RESET);
  314.         if (!$configuration->isHtmlRequest()) {
  315.             return $this->viewHandler->handle($configurationView::create(nullResponse::HTTP_NO_CONTENT));
  316.         }
  317.         $redirectRouteName $this->getSyliusAttribute($request'redirect'null);
  318.         Assert::notNull($redirectRouteName'Redirect is not configured.');
  319.         return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
  320.     }
  321.     protected function handleChangePassword(
  322.         Request $request,
  323.         RequestConfiguration $configuration,
  324.         UserInterface $user,
  325.         string $newPassword
  326.     ): Response {
  327.         $user->setPlainPassword($newPassword);
  328.         $dispatcher $this->container->get('event_dispatcher');
  329.         $dispatcher->dispatch(new GenericEvent($user), UserEvents::PRE_PASSWORD_CHANGE);
  330.         $this->manager->flush();
  331.         $this->addTranslatedFlash('success''sylius.user.change_password');
  332.         $dispatcher->dispatch(new GenericEvent($user), UserEvents::POST_PASSWORD_CHANGE);
  333.         if (!$configuration->isHtmlRequest()) {
  334.             return $this->viewHandler->handle($configurationView::create(nullResponse::HTTP_NO_CONTENT));
  335.         }
  336.         $redirectRouteName $this->getSyliusAttribute($request'redirect'null);
  337.         Assert::notNull($redirectRouteName'Redirect is not configured.');
  338.         return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
  339.     }
  340.     protected function getUser(): ?UserInterface
  341.     {
  342.         $user parent::getUser();
  343.         $authorizationChecker $this->container->get('security.authorization_checker');
  344.         if (
  345.             $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED') &&
  346.             $user instanceof UserInterface
  347.         ) {
  348.             return $user;
  349.         }
  350.         return null;
  351.     }
  352.     private function getSyliusAttribute(Request $requeststring $attribute$default null)
  353.     {
  354.         $attributes $request->attributes->get('_sylius');
  355.         return $attributes[$attribute] ?? $default;
  356.     }
  357. }