src/Controller/ResetPasswordController.php line 90

Open in your IDE?
  1. <?php
  2.     namespace App\Controller;
  3.     use App\Entity\User;
  4.     use App\Event\PortalUserEvent;
  5.     use App\Factory\Platform\MailerFactory;
  6.     use App\Form\Type\ChangePasswordFormType;
  7.     use App\Form\Type\ResetPasswordRequestFormType;
  8.     use App\Services\Common\Email\MailTypes;
  9.     use App\Services\Common\MailerService;
  10.     use App\Services\Common\User\WorkflowUser;
  11.     use App\Services\DTV\YamlConfig\YamlReader;
  12.     use App\Services\Portal\PortalService;
  13.     use Doctrine\ORM\EntityManagerInterface;
  14.     use Exception;
  15.     use Psr\Log\LoggerInterface;
  16.     use ReflectionException;
  17.     use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  18.     use Symfony\Component\HttpFoundation\RedirectResponse;
  19.     use Symfony\Component\HttpFoundation\Request;
  20.     use Symfony\Component\HttpFoundation\Response;
  21.     use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  22.     use Symfony\Component\Routing\Annotation\Route;
  23.     use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  24.     use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  25.     use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  26.     use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  27.     use Symfony\Contracts\Translation\TranslatorInterface;
  28.     use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
  29.     use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
  30.     use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
  31.     /**
  32.      * Controller pour la gestion de la réinitialisation du mot de passe
  33.      *
  34.      * @Route("/reset-password")
  35.      */
  36.     class ResetPasswordController extends AbstractController
  37.     {
  38.         use ResetPasswordControllerTrait;
  39.         private ResetPasswordHelperInterface $resetPasswordHelper;
  40.         private LoggerInterface              $logger;
  41.         private MailerService                $mailerService;
  42.         private YamlReader                   $yamlReader;
  43.         private EntityManagerInterface       $em;
  44.         private PortalService                $portalService;
  45.         private EventDispatcherInterface     $dispatcher;
  46.         private TranslatorInterface    $translator;
  47.         private WorkflowUser $workflowUser;
  48.         public function __construct(
  49.                 ResetPasswordHelperInterface $resetPasswordHelper,
  50.                 LoggerInterface $logger,
  51.                 MailerService $mailerService,
  52.                 YamlReader $yamlReader,
  53.                 EntityManagerInterface $em,
  54.                 PortalService $portalService,
  55.                 EventDispatcherInterface $dispatcher,
  56.                 TranslatorInterface $translator,
  57.                 WorkflowUser $workflowUser
  58.         ) {
  59.             $this->resetPasswordHelper $resetPasswordHelper;
  60.             $this->logger              $logger;
  61.             $this->mailerService       $mailerService;
  62.             $this->yamlReader          $yamlReader;
  63.             $this->em                  $em;
  64.             $this->portalService       $portalService;
  65.             $this->dispatcher          $dispatcher;
  66.             $this->translator $translator;
  67.             $this->workflowUser $workflowUser;
  68.         }
  69.         /**
  70.          * Affiche et traite le formulaire de demande de réinitialisation du mot de passe.
  71.          *
  72.          * @Route("", name="app_forgot_password_request")
  73.          *
  74.          * @param Request $request
  75.          *
  76.          * @return Response
  77.          *
  78.          * @throws ClientExceptionInterface
  79.          * @throws RedirectionExceptionInterface
  80.          * @throws ReflectionException
  81.          * @throws ServerExceptionInterface
  82.          */
  83.         public function request(Request $request): Response
  84.         {
  85.             $form $this->createForm(ResetPasswordRequestFormType::class);
  86.             $form->handleRequest($request);
  87.             if ($form->isSubmitted() && $form->isValid()) {
  88.                 return $this->processSendingPasswordResetEmail(
  89.                         $form->get('email')->getData(),
  90.                 );
  91.             }
  92.             return $this->render('security/request.html.twig', [
  93.                     'requestForm' => $form->createView(),
  94.             ]);
  95.         }
  96.         /**
  97.          * Redirige vers la page de vérification de l'e-mail.
  98.          *
  99.          * @param string $emailFormData
  100.          *
  101.          * @return RedirectResponse
  102.          *
  103.          * @throws ReflectionException
  104.          * @throws ClientExceptionInterface
  105.          * @throws RedirectionExceptionInterface
  106.          * @throws ServerExceptionInterface
  107.          * @throws Exception
  108.          */
  109.         private function processSendingPasswordResetEmail(string $emailFormData): RedirectResponse
  110.         {
  111.             $user $this->em->getRepository(User::class)->findOneBy(
  112.                     [
  113.                             'email' => $emailFormData,
  114.                     ],
  115.             );
  116.             // Do not reveal whether a user account was found or not.
  117.             if ($user === NULL) {
  118.                 return $this->redirectToRoute('app_check_email');
  119.             }
  120.             $mailer $this->yamlReader->getMailer();
  121.             // Check les CGU
  122.             if (is_null($user->getCguAt()) && $user->getStatus() === User::STATUS_CGU_PENDING) {
  123.                 $this->workflowUser->resendCGU($user);
  124.                 $this->addFlash(
  125.                         'danger',
  126.                         $this->translator->trans(
  127.                         "cgu_pending_message %contact_email%",
  128.                             ["%contact_email%" => $mailer'contact_email' ]]
  129.                         )
  130.                 );
  131.                 return $this->redirectToRoute('app_login');
  132.             }
  133.             try {
  134.                 $resetToken $this->resetPasswordHelper->generateResetToken($user);
  135.             } catch (ResetPasswordExceptionInterface $e) {
  136.                 // If you want to tell the user why a reset email was not sent, uncomment
  137.                 // the lines below and change the redirect to 'app_forgot_password_request'.
  138.                 // Caution: This may reveal if a user is registered or not.
  139.                 $this->addFlash(
  140.                         'reset_password_error',
  141.                         'il y a eu un problème lors du traitement de votre demande de réinitialisation du mot de passe - ' $e->getReason(
  142.                         ),
  143.                 );
  144.                 return $this->redirectToRoute('app_check_email');
  145.             }
  146.             $this->mailerService->createApiMailRequest(MailTypes::RESET_PASSWORD)
  147.                                 ->addRecipientToRequest(
  148.                                         $user,
  149.                                         MailerFactory::buildResetPassword($resetToken->getToken()),
  150.                                 )
  151.                                 ->send()
  152.             ;
  153.             // Store the token object in session for retrieval in check-email route.
  154.             $this->setTokenObjectInSession($resetToken);
  155.             return $this->redirectToRoute('app_check_email');
  156.         }
  157.         /**
  158.          * Page de confirmation après qu'un utilisateur a demandé une réinitialisation du mot de passe.
  159.          *
  160.          * @Route("/check-email", name="app_check_email")
  161.          */
  162.         public function checkEmail(): Response
  163.         {
  164.             return $this->render('security/check_email.html.twig', [
  165.                     'resetToken' => $this->getTokenObjectFromSession(),
  166.             ]);
  167.         }
  168.         /**
  169.          * Valide et traite l'URL de réinitialisation que l'utilisateur a cliqué dans son e-mail.
  170.          *
  171.          * @Route("/reset/{token}", name="app_reset_password")
  172.          */
  173.         public function reset(Request $requestUserPasswordHasherInterface $passwordEncoderstring $token NULL): Response
  174.         {
  175.             if ($token) {
  176.                 // We store the token in session and remove it from the URL, to avoid the URL being
  177.                 // loaded in a browser and potentially leaking the token to 3rd party JavaScript.
  178.                 $this->storeTokenInSession($token);
  179.                 return $this->redirectToRoute('app_reset_password');
  180.             }
  181.             $token $this->getTokenFromSession();
  182.             if (NULL === $token) {
  183.                 throw $this->createNotFoundException('No reset password token found in the URL or in the session.');
  184.             }
  185.             try {
  186.                 /** @var User $user */
  187.                 $user $this->resetPasswordHelper->validateTokenAndFetchUser($token);
  188.             } catch (ResetPasswordExceptionInterface $e) {
  189.                 $this->addFlash(
  190.                         'reset_password_error',
  191.                             $this->translator->trans(
  192.                             'il y a eu un problème lors de la validation de votre demande de réinitialisation - %s %reason%',
  193.                             ['%reason%' => $e->getReason()]
  194.                         ),
  195.                 );
  196.                 return $this->redirectToRoute('app_forgot_password_request');
  197.             }
  198.             // The token is valid; allow the user to change their password.
  199.             $form $this->createForm(ChangePasswordFormType::class);
  200.             $form->handleRequest($request);
  201.             if ($form->isSubmitted() && $form->isValid()) {
  202.                 $isPortal $this->portalService->isOnPortal();
  203.                 // A password reset token should be used only once, remove it.
  204.                 $this->resetPasswordHelper->removeResetRequest($token);
  205.                 // Encode the plain password, and set it.
  206.                 $encodedPassword $passwordEncoder->hashPassword(
  207.                         $user,
  208.                         $form->get('plainPassword')->getData(),
  209.                 );
  210.                 // Si on est sur un portail (parent), on propage le nouveau mot de passe sur tous les sites enfants
  211.                 if ($isPortal && $this->portalService->isAParent()) {
  212.                     $user->setPlainPassword($form->get('plainPassword')->getData());
  213.                     $portalUserEvent = new PortalUserEvent($user);
  214.                     $this->dispatcher->dispatch($portalUserEvent$portalUserEvent::NAME);
  215.                 }
  216.                 $user->setPassword($encodedPassword);
  217.                 $user->setFailedAttempts(0);
  218.                 $user->setLastFailedAttempt(null);
  219.                 $this->em->flush();
  220.                 // The session is cleaned up after the password has been changed.
  221.                 $this->cleanSessionAfterReset();
  222.                 $this->addFlash(
  223.                         'success',
  224.                         $this->translator->trans('votre nouveau mot de passe a bien été enregistré !')
  225.                 );
  226.                 return $this->redirectToRoute('app_login');
  227.             }
  228.             return $this->render('security/reset.html.twig', [
  229.                     'resetForm' => $form->createView(),
  230.             ]);
  231.         }
  232.         /**
  233.          * Indique si on doit mettre à jour le mot de passe de l'utilisateur
  234.          *
  235.          * @Route("/{id}", name="app_need_refresh_password")
  236.          *
  237.          *
  238.          * @param User $user
  239.          *
  240.          * @return Response
  241.          *
  242.          * @throws ClientExceptionInterface
  243.          * @throws RedirectionExceptionInterface
  244.          * @throws ReflectionException
  245.          * @throws ServerExceptionInterface
  246.          * @throws Exception
  247.          * @throws Exception
  248.          */
  249.         public function needRefreshPassword(User $user): Response
  250.         {
  251.             try {
  252.                 $resetToken $this->resetPasswordHelper->generateResetToken($user);
  253.             } catch (ResetPasswordExceptionInterface $e) {
  254.                 return $this->render('security/need_refresh_password.html.twig');
  255.             }
  256.             $this->mailerService->createApiMailRequest(MailTypes::RESET_PASSWORD)
  257.                                 ->addRecipientToRequest(
  258.                                         $user,
  259.                                         MailerFactory::buildResetPassword(
  260.                                                 $resetToken->getToken(),
  261.                                         ),
  262.                                 )
  263.                                 ->send()
  264.             ;
  265.             // Store the token object in session for retrieval in check-email route.
  266.             $this->setTokenObjectInSession($resetToken);
  267.             return $this->render('security/need_refresh_password.html.twig', [
  268.                     'resetToken' => $resetToken,
  269.             ]);
  270.         }
  271.     }