src/Controller/ResetPasswordController.php line 39

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