src/Controller/SecurityController.php line 111

  1. <?php
  2. namespace App\Controller;
  3. use App\Event\AppEvents;
  4. use App\Event\UserResetPasswordEvent;
  5. use App\Model\Form\ChangePasswordForm;
  6. use App\Model\Form\ResetPasswordForm;
  7. use App\Repository\UserRepository;
  8. use DateTime;
  9. use App\Entity\User;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use Ecommerce121\UtilBundle\Controller\ControllerBase;
  12. use Ecommerce121\UtilBundle\Controller\ControllerUtil;
  13. use Symfony\Component\HttpKernel\Attribute\AsController;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\HttpFoundation\Session\Session;
  16. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  17. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  18. use Symfony\Component\HttpFoundation\Response;
  19. use Symfony\Component\Routing\Attribute\Route;
  20. use Twig\Error\LoaderError;
  21. use Twig\Error\RuntimeError;
  22. use Twig\Error\SyntaxError;
  23. #[AsController]
  24. class SecurityController extends ControllerBase
  25. {
  26. public function __construct(
  27. ControllerUtil $controllerUtil,
  28. private readonly AuthenticationUtils $authenticationUtils,
  29. private readonly UserPasswordHasherInterface $passwordHasher,
  30. private readonly EntityManagerInterface $entityManager,
  31. private readonly UserRepository $userRepository
  32. ) {
  33. parent::__construct($controllerUtil);
  34. }
  35. /**
  36. * @throws SyntaxError
  37. * @throws RuntimeError
  38. * @throws LoaderError
  39. */
  40. #[Route('/login', name: 'app_login')]
  41. public function loginAction(): Response
  42. {
  43. if ($this->getUser() instanceof User) {
  44. $dashboardPath = $this->generateUrl('app_home');
  45. return $this->redirect($dashboardPath);
  46. }
  47. // Pass the last username and error to the template
  48. return $this->render('Security/login.html.twig', [
  49. 'last_username' => $this->authenticationUtils->getLastUsername(),
  50. 'error' => $this->authenticationUtils->getLastAuthenticationError(),
  51. ]);
  52. }
  53. #[Route('/loginCheck', name: 'app_login_check')]
  54. public function loginCheckAction()
  55. {
  56. throw new \RuntimeException('This should never be reached!');
  57. }
  58. #[Route('/logout', name: 'app_logout')]
  59. public function logoutAction()
  60. {
  61. throw new \RuntimeException('This should never be reached!');
  62. }
  63. /**
  64. * @throws SyntaxError
  65. * @throws RuntimeError
  66. * @throws LoaderError
  67. */
  68. #[Route('/resetPassword', name: 'app_reset_password')]
  69. public function resetPasswordAction(Request $request): Response
  70. {
  71. $form = $this->createForm(ResetPasswordForm::class);
  72. $form->handleRequest($request);
  73. if ($form->isSubmitted() && $form->isValid()) {
  74. $email = $form->get('email')->getData();
  75. $user = $this->userRepository->findOneBy(['email' => $email]);
  76. if ($user instanceof User) {
  77. $user->setForgetPasswordValidUntil(new DateTime('+7 days'));
  78. $user->setForgetPasswordCode(uniqid());
  79. $this->entityManager->persist($user);
  80. $this->entityManager->flush();
  81. $this->dispatchEvent(AppEvents::USER_RESET_PASSWORD, new UserResetPasswordEvent($user));
  82. }
  83. }
  84. return $this->render('Security/resetPassword.html.twig', [
  85. 'form' => $form->createView(),
  86. 'formIsValid' => $form->isSubmitted() && $form->isValid(),
  87. ]);
  88. }
  89. /**
  90. * @throws SyntaxError
  91. * @throws RuntimeError
  92. * @throws LoaderError
  93. */
  94. #[Route('/changePassword/{code}', name: 'app_change_password')]
  95. public function changePasswordAction(string $code, Request $request): Response
  96. {
  97. return $this->handlePasswordChange($code, $request, 'resetPassword');
  98. }
  99. /**
  100. * @throws SyntaxError
  101. * @throws RuntimeError
  102. * @throws LoaderError
  103. */
  104. #[Route('/update-password/{code}', name: 'app_update_user_password')]
  105. public function updateUserPassword(string $code, Request $request): Response
  106. {
  107. return $this->handlePasswordChange($code, $request, 'updatePassword');
  108. }
  109. /**
  110. * @throws SyntaxError
  111. * @throws RuntimeError
  112. * @throws LoaderError
  113. */
  114. private function handlePasswordChange(string $code, Request $request, string $passwordAction): Response
  115. {
  116. $this->forward404Unless($code);
  117. $user = $this->userRepository->findOneBy(['forgetPasswordCode' => $code]);
  118. $oldPassword = $user?->getPassword();
  119. $session = $request->getSession();
  120. $homeUrl = $this->generateUrl('app_home');
  121. $updatePassUrl = $this->generateUrl('app_update_user_password', ['code' => $code]);
  122. if (!$user instanceof User) {
  123. /** @var Session $session */
  124. $session = $request->getSession();
  125. $session->getFlashBag()->add('message_password', 'Your code has been already used or expired.');
  126. return $this->redirect($homeUrl);
  127. }
  128. $form = $this->createForm(ChangePasswordForm::class);
  129. $form->handleRequest($request);
  130. if ($form->isSubmitted() && $form->isValid()) {
  131. $newPassword = $form['newPassword']->getData();
  132. if ($this->passwordHasher->isPasswordValid($user, $newPassword)) {
  133. $session->getFlashBag()->add('message_password', 'Please use a different password. Old and new passwords are the same.');
  134. return $this->redirect($updatePassUrl);
  135. }
  136. $encodedPassword = $this->passwordHasher->hashPassword($user, $newPassword);
  137. $user->changePassword($encodedPassword);
  138. $user->setForgetPasswordCode(null);
  139. $user->setForgetPasswordValidUntil(null);
  140. $user->setPasswordValidUntil(new \DateTime('+90 days'));
  141. $this->entityManager->persist($user);
  142. $this->entityManager->flush();
  143. $session->getFlashBag()->add('message_password', 'Your password has been updated.');
  144. return $this->redirect($homeUrl);
  145. }
  146. return $this->render('Security/changePassword.html.twig', [
  147. 'form' => $form->createView(),
  148. 'code' => $code,
  149. 'user' => $user,
  150. 'updatePassword' => ($passwordAction === 'updatePassword'),
  151. ]);
  152. }
  153. }