src/Controller/Admin/Security/ChangePassword/ChangePasswordController.php line 43

Open in your IDE?
  1. <?php
  2. /**
  3.  * @author Yenier Jimenez <yjmorales86@gmail.com>
  4.  */
  5. namespace App\Controller\Admin\Security\ChangePassword;
  6. use App\Api\Core\Model\ApiEmptyResponse;
  7. use App\Controller\Core\BaseController;
  8. use App\Entity\User;
  9. use App\Form\Admin\Unauthenticated\User\ChangePasswordFormType;
  10. use Common\Communication\HtmlMailer\Mailer;
  11. use Common\Communication\HtmlMailer\MailerMessage;
  12. use Common\DataManagement\Validator\DataValidator;
  13. use Common\DataStorage\Redis\RedisCacheRegistry;
  14. use Common\Security\AntiSpam\ReCaptcha\v3\Exception\ReCaptchaV3Exception;
  15. use Common\Security\AntiSpam\ReCaptcha\v3\ReCaptchaV3Validator;
  16. use Doctrine\Persistence\ManagerRegistry;
  17. use Exception;
  18. use stdClass;
  19. use Symfony\Component\HttpFoundation\JsonResponse;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Response;
  22. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  23. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  24. use Symfony\Component\Routing\RouterInterface;
  25. use Symfony\Component\Routing\Annotation\Route;
  26. /**
  27.  * Controller to change password.
  28.  *
  29.  * @Route("/admin/change-password")
  30.  */
  31. class ChangePasswordController extends BaseController
  32. {
  33.     /**
  34.      * Responsible to render the page to enter the email.
  35.      *
  36.      * @Route("/enter-email", name="bo_change_password_enter_email")
  37.      *
  38.      * @return Response
  39.      */
  40.     public function enterEmail(): Response
  41.     {
  42.         return $this->render('/admin/unauthenticated/security/recover_password/change_password_enter_email.html.twig', [
  43.             'recaptchaV3SiteKey' => $this->getParameter('operation_cpg_recaptcha_v3_site_key'),
  44.         ]);
  45.     }
  46.     /**
  47.      * Responsible to generate and send the change password link.
  48.      *
  49.      * @Route("/send-link", name="bo_change_password_send_link")
  50.      *
  51.      * @param Request              $request
  52.      * @param Mailer               $mailer
  53.      * @param RedisCacheRegistry   $cache
  54.      * @param RouterInterface      $router
  55.      * @param ManagerRegistry      $doctrine
  56.      * @param ReCaptchaV3Validator $reCaptchaV3Validator
  57.      *
  58.      * @return Response
  59.      *
  60.      * @throws ReCaptchaV3Exception
  61.      * @throws Exception
  62.      */
  63.     public function sendLink(
  64.         Request $request,
  65.         Mailer $mailer,
  66.         RedisCacheRegistry $cache,
  67.         RouterInterface $router,
  68.         ManagerRegistry $doctrine,
  69.         ReCaptchaV3Validator $reCaptchaV3Validator
  70.     ): Response {
  71.         $validRecaptcha $reCaptchaV3Validator->validateByUserAction();
  72.         if (!$validRecaptcha) {
  73.             throw new Exception('You are a robot.');
  74.         }
  75.         $valid $this->validator()->isValidString($email $request->get('email'));
  76.         if (!$valid) {
  77.             throw new Exception('The email is invalid.');
  78.         }
  79.         $ttlMinutes   15;
  80.         $hash         md5(uniqid());
  81.         $route        $router->generate('bo_change_password_enter_new_password', ['hash' => $hash]);
  82.         $link         $_SERVER['HTTP_ORIGIN'] . $route;
  83.         $user         $this->repository($doctrineUser::class)->findOneByEmail($email);
  84.         $data         = new stdClass();
  85.         $data->userId $user->getId();
  86.         $data->link   $link;
  87.         $cache->set($hash$data$ttlMinutes 60); // Save hash for 15 minutes.
  88.         $title $this->getParameter('system_name');
  89.         $msg   = new MailerMessage("$title - Recover password."$email);
  90.         $msg->setContext([
  91.             'emailAddress' => $email,
  92.             'link'         => $data->link,
  93.             'ttlMinutes'   => $ttlMinutes,
  94.         ]);
  95.         $msg->setHtmlTemplate('/admin/email/security/change_password/change_password_link.html.twig');
  96.         if (!$mailer->send($msg)) {
  97.             throw new Exception('The link to change the password was not able to be sent.');
  98.         }
  99.         return $this->buildJsonResponse(new ApiEmptyResponse());
  100.     }
  101.     /**
  102.      * Updates the password.
  103.      *
  104.      * @Route("/enter-new-password", name="bo_change_password_enter_new_password")
  105.      *
  106.      * @param Request            $request
  107.      * @param RedisCacheRegistry $cache
  108.      *
  109.      * @return JsonResponse
  110.      *
  111.      * @throws Exception
  112.      */
  113.     public function enterNewPassword(Request $requestRedisCacheRegistry $cache): Response
  114.     {
  115.         $title       $this->getParameter('system_name');
  116.         $form        $this->createForm(ChangePasswordFormType::class);
  117.         $hash        $request->get('hash');
  118.         $noSentHash  = !$hash;
  119.         $expiredHash = !$cache->get($hash);
  120.         return $this->render('/admin/unauthenticated/security/recover_password/change_password_enter_new_password.html.twig',
  121.             [
  122.                 'title'              => $title,
  123.                 'noSentHash'         => $noSentHash,
  124.                 'expiredHash'        => $expiredHash,
  125.                 'form'               => $form->createView(),
  126.                 'recaptchaV3SiteKey' => $this->getParameter('operation_cpg_recaptcha_v3_site_key'),
  127.                 'hash'               => $hash
  128.             ]);
  129.     }
  130.     /**
  131.      * Updates the password.
  132.      *
  133.      * @Route("/update", name="bo_change_password_update")
  134.      *
  135.      * @param Request                     $request
  136.      * @param ManagerRegistry             $doctrine
  137.      * @param RedisCacheRegistry          $cache
  138.      * @param UserPasswordHasherInterface $passwordHasher
  139.      * @param Mailer                      $mailer
  140.      * @param ReCaptchaV3Validator        $reCaptchaV3Validator
  141.      *
  142.      * @return JsonResponse
  143.      *
  144.      * @throws ReCaptchaV3Exception
  145.      * @throws Exception
  146.      */
  147.     public function update(
  148.         Request $request,
  149.         ManagerRegistry $doctrine,
  150.         RedisCacheRegistry $cache,
  151.         UserPasswordHasherInterface $passwordHasher,
  152.         Mailer $mailer,
  153.         ReCaptchaV3Validator $reCaptchaV3Validator
  154.     ): Response {
  155.         $validRecaptcha $reCaptchaV3Validator->validateByUserAction();
  156.         if (!$validRecaptcha) {
  157.             throw new Exception('You are a robot.');
  158.         }
  159.         $hash $request->get('hash');
  160.         if (!$cacheData $cache->get($hash)) {
  161.             $this->notifyError('The token to recover the password is already expired.');
  162.             throw new NotFoundHttpException('The hash is invalid');
  163.         }
  164.         $title $this->getParameter('system_name');
  165.         $form  $this->createForm(ChangePasswordFormType::class);
  166.         $form->handleRequest($request);
  167.         if (!$form->isSubmitted() || !$form->isValid()) {
  168.             $this->notifyError('The password you submitted is invalid.');
  169.             return $this->redirectToRoute('bo_change_password_enter_new_password', ['hash' => $hash]);
  170.         }
  171.         $password $form->get('password')->getData();
  172.         $isValid  $this->validator()->isValidString($password);
  173.         if (!$isValid) {
  174.             $this->notifyError('The password is invalid.');
  175.             return $this->redirectToRoute('bo_change_password_enter_new_password', ['hash' => $hash]);
  176.         }
  177.         $user $this->repository($doctrineUser::class)->find($cacheData->userId);
  178.         $user->setPassword($passwordHasher->hashPassword($user$password));
  179.         $this->em($doctrine)->persist($user);
  180.         $this->em($doctrine)->flush();
  181.         $msg = new MailerMessage("$title - Password changed."$user->getEmail());
  182.         $msg->setHtmlTemplate('/admin/email/security/change_password/change_password_notification.html.twig');
  183.         $mailer->send($msg);
  184.         return $this->redirectToRoute('bo_change_password_success', ['hash' => $hash]);
  185.     }
  186.     /**
  187.      * Renders a page to display that the password has been updated successfully.
  188.      *
  189.      * @Route("/success/{hash}", name="bo_change_password_success")
  190.      * @throws Exception
  191.      */
  192.     public function success(Request $requestRedisCacheRegistry $cache): Response
  193.     {
  194.         if (!$hash $request->get('hash')) {
  195.             return $this->redirectToRoute('bo_login');
  196.         }
  197.         if (!$cache->get($hash)) {
  198.             return $this->redirectToRoute('bo_login');
  199.         }
  200.         $cache->purge($hash);
  201.         return $this->render('/admin/unauthenticated/security/recover_password/changed_password_successfully.html.twig',
  202.             [
  203.                 'recaptchaV3SiteKey' => $this->getParameter('operation_cpg_recaptcha_v3_site_key'),
  204.             ]);
  205.     }
  206. }