src/Controller/ResetPasswordController.php line 42

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\User;
  4. use App\Form\ChangePasswordFormType;
  5. use App\Form\ResetPasswordRequestFormType;
  6. use App\Service\APIMailer;
  7. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  8. use Symfony\Component\HttpFoundation\RedirectResponse;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  13. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  14. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  15. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  16. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  17. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  18. use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
  19. use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
  20. use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
  21. #[Route(path: '/reset-password')]
  22. class ResetPasswordController extends AbstractController
  23. {
  24.     use ResetPasswordControllerTrait;
  25.     public function __construct(private ResetPasswordHelperInterface $resetPasswordHelper)
  26.     {
  27.     }
  28.     /**
  29.      * Display & process form to request a password reset.
  30.      *
  31.      * @throws TransportExceptionInterface
  32.      * @throws ServerExceptionInterface
  33.      * @throws RedirectionExceptionInterface
  34.      * @throws ClientExceptionInterface
  35.      */
  36.     #[Route(path: '', name: 'app_forgot_password_request')]
  37.     public function request(Request $request, APIMailer $mailer): Response
  38.     {
  39.         $form = $this->createForm(ResetPasswordRequestFormType::class);
  40.         $form->handleRequest($request);
  41.         if ($form->isSubmitted() && $form->isValid()) {
  42.             return $this->processSendingPasswordResetEmail(
  43.                 $form->get('email')->getData(),
  44.                 $mailer
  45.             );
  46.         }
  47.         return $this->render('reset_password/request.html.twig', [
  48.             'requestForm' => $form->createView(),
  49.         ]);
  50.     }
  51.     /**
  52.      * Confirmation page after a user has requested a password reset.
  53.      */
  54.     #[Route(path: '/check-email', name: 'app_check_email')]
  55.     public function checkEmail(): Response
  56.     {
  57.         // We prevent users from directly accessing this page
  58.         if (!$this->canCheckEmail()) {
  59.             return $this->redirectToRoute('app_forgot_password_request');
  60.         }
  61.         return $this->render('reset_password/check_email.html.twig', [
  62.             'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime(),
  63.         ]);
  64.     }
  65.     /**
  66.      * Validates and process the reset URL that the user clicked in their email.
  67.      */
  68.     #[Route(path: '/reset/{token}', name: 'app_reset_password')]
  69.     public function reset(Request $request, UserPasswordEncoderInterface $passwordEncoder, ?string $token = null): Response
  70.     {
  71.         if ($token) {
  72.             // We store the token in session and remove it from the URL, to avoid the URL being
  73.             // loaded in a browser and potentially leaking the token to 3rd party JavaScript.
  74.             $this->storeTokenInSession($token);
  75.             return $this->redirectToRoute('app_reset_password');
  76.         }
  77.         $token = $this->getTokenFromSession();
  78.         if (null === $token) {
  79.             throw $this->createNotFoundException('No reset password token found in the URL or in the session.');
  80.         }
  81.         try {
  82.             $user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
  83.         } catch (ResetPasswordExceptionInterface $e) {
  84.             $this->addFlash('reset_password_error', sprintf(
  85.                 'There was a problem validating your reset request - %s',
  86.                 $e->getReason()
  87.             ));
  88.             return $this->redirectToRoute('app_forgot_password_request');
  89.         }
  90.         // The token is valid; allow the user to change their password.
  91.         $form = $this->createForm(ChangePasswordFormType::class);
  92.         $form->handleRequest($request);
  93.         if ($form->isSubmitted() && $form->isValid()) {
  94.             // A password reset token should be used only once, remove it.
  95.             $this->resetPasswordHelper->removeResetRequest($token);
  96.             // Encode the plain password, and set it.
  97.             $encodedPassword = $passwordEncoder->encodePassword(
  98.                 $user,
  99.                 $form->get('plainPassword')->getData()
  100.             );
  101.             $user->setPassword($encodedPassword);
  102.             $this->getDoctrine()->getManager()->flush();
  103.             // The session is cleaned up after the password has been changed.
  104.             $this->cleanSessionAfterReset();
  105.             return $this->redirectToRoute('cms_homepage');
  106.         }
  107.         return $this->render('reset_password/reset.html.twig', [
  108.             'resetForm' => $form->createView(),
  109.         ]);
  110.     }
  111.     /**
  112.      * @throws TransportExceptionInterface
  113.      * @throws ServerExceptionInterface
  114.      * @throws RedirectionExceptionInterface
  115.      * @throws ClientExceptionInterface
  116.      */
  117.     private function processSendingPasswordResetEmail(string $emailFormData, APIMailer $mailer): RedirectResponse
  118.     {
  119.         $user = $this->getDoctrine()->getRepository(User::class)->findOneBy([
  120.             'email' => $emailFormData,
  121.         ]);
  122.         // Marks that you are allowed to see the app_check_email page.
  123.         $this->setCanCheckEmailInSession();
  124.         // Do not reveal whether a user account was found or not.
  125.         if (!$user) {
  126.             return $this->redirectToRoute('app_check_email');
  127.         }
  128.         try {
  129.             $resetToken = $this->resetPasswordHelper->generateResetToken($user);
  130.             $reset_url = $this->generateUrl('app_reset_password', [
  131.                 'token' => $resetToken->getToken(),
  132.             ], UrlGeneratorInterface::ABSOLUTE_URL);
  133.             $mailer->sendEmail(
  134.                 $this->getParameter('email_reset_password'),
  135.                 ['url_reset' => $reset_url, 'user' => $user]
  136.             );
  137.         } catch (ResetPasswordExceptionInterface) {
  138.             return $this->redirectToRoute('app_check_email');
  139.         }
  140.         return $this->redirectToRoute('app_check_email');
  141.     }
  142. }