src/Controller/Home/authentication/recover/RecoverRequestEmailController.php line 27

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Home\authentication\recover;
  3. use App\Exception\AuthenticationException;
  4. use App\Exception\InvalidParameterException;
  5. use App\Interfaces\UsuarioHermesRepositoryInterface;
  6. use App\Services\AsymmetricEncryptionService;
  7. use Doctrine\Common\Collections\Criteria;
  8. use Psr\Log\LoggerInterface;
  9. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  13. use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
  14. use Symfony\Component\Mailer\MailerInterface;
  15. use Symfony\Component\Mime\Address;
  16. use Symfony\Component\Mime\Email;
  17. use Symfony\Component\Routing\Annotation\Route;
  18. use Symfony\Component\Routing\RouterInterface;
  19. use Symfony\Contracts\Translation\TranslatorInterface;
  20. use Twig\Environment;
  21. /**
  22. * Controlador para solicitar la recuperación de contraseña por email
  23. */
  24. class RecoverRequestEmailController extends AbstractController
  25. {
  26. /**
  27. * @param SessionInterface $session Servicio de sesión
  28. * @param AsymmetricEncryptionService $asymmetricEncryptionService Servicio de encriptación asimétrica
  29. * @param MailerInterface $mailer Servicio de correo electrónico
  30. * @param RouterInterface $router Servicio de enrutamiento
  31. * @param Environment $templating Servicio de plantillas Twig
  32. * @param UsuarioHermesRepositoryInterface $usuarioHermesRepository Repositorio de usuarios
  33. * @param TranslatorInterface $translator Servicio de traducción
  34. * @param LoggerInterface $logger Servicio de logging
  35. */
  36. public function __construct(
  37. private SessionInterface $session,
  38. private AsymmetricEncryptionService $asymmetricEncryptionService,
  39. private MailerInterface $mailer,
  40. private RouterInterface $router,
  41. private Environment $templating,
  42. private UsuarioHermesRepositoryInterface $usuarioHermesRepository,
  43. private TranslatorInterface $translator,
  44. private LoggerInterface $logger
  45. ) { }
  46. /**
  47. * Procesa la solicitud de recuperación de contraseña por email
  48. *
  49. * @Route("/{_locale}/recover/request/email", name="recover_request_email", defaults={"_locale" = "ca"}, requirements={"_locale": "es|ca"}, methods={"GET", "POST"})
  50. *
  51. * @param Request $request Objeto Request de Symfony
  52. *
  53. * @return Response
  54. *
  55. * @throws InvalidParameterException Si el email no es válido o está vacío
  56. * @throws AuthenticationException Si no existe un usuario con el email proporcionado
  57. */
  58. public function __invoke(Request $request): Response
  59. {
  60. try {
  61. // Si no es una solicitud POST, simplemente renderizar el formulario
  62. if (!$request->isMethod('POST')) {
  63. return $this->render('home/authentication/recover/recover_request_email.html.twig');
  64. }
  65. // Obtener y validar el email
  66. $email = $request->request->get('email');
  67. if (empty($email)) {
  68. throw new InvalidParameterException('El email es obligatorio');
  69. }
  70. // Validar formato de email
  71. if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  72. throw new InvalidParameterException('El formato del email no es válido');
  73. }
  74. // Buscar usuario por email
  75. $criteria = (new Criteria())->andWhere(Criteria::expr()->eq('email', $email));
  76. $usuarioCollection = $this->usuarioHermesRepository->find($criteria);
  77. // Verificar que existe un usuario con ese email
  78. if (!$usuarioCollection || $usuarioCollection->isEmpty()) {
  79. throw new AuthenticationException("No existe ninguna cuenta con el email {$email}");
  80. }
  81. // Crear token de recuperación con tiempo de expiración
  82. $tokenData = json_encode([
  83. 'ts' => time(),
  84. 'email' => $email
  85. ]);
  86. // Encriptar token
  87. $token = $this->asymmetricEncryptionService->privateEncrypt($tokenData);
  88. // Generar link de recuperación
  89. $link = $this->router->generate('recover_request_new_password', ['token' => $token], RouterInterface::ABSOLUTE_URL);
  90. // Preparar y enviar email
  91. $emailFrom = $_ENV['EMAIL_FROM_ADDRESS'] ?? "no-reply@conforcat.cat";
  92. $nameFrom = $_ENV['EMAIL_FROM_NAME'] ?? "Conforcat";
  93. $emailMessage = (new Email())
  94. ->from(new Address($emailFrom, $nameFrom))
  95. ->to($email)
  96. ->subject($this->translator->trans('email.subject.recover_password', [], 'recover_password'))
  97. ->html($this->templating->render(
  98. 'home/authentication/recover/email_recover_token_sent.html.twig',
  99. ['link' => $link]
  100. ));
  101. // Enviar email
  102. $this->mailer->send($emailMessage);
  103. // Renderizar página de confirmación
  104. return $this->render('home/authentication/recover/recover_token_sent.html.twig', [
  105. 'email' => $email
  106. ]);
  107. } catch (InvalidParameterException | AuthenticationException $e) {
  108. // Manejar excepciones no críticas
  109. $this->addFlash('error', $e->getMessage());
  110. $this->logger->warning('Error en solicitud de recuperación: ' . $e->getMessage());
  111. // Volver al formulario
  112. return $this->render('home/authentication/recover/recover_request_email.html.twig');
  113. } catch (TransportExceptionInterface $e) {
  114. // Manejar errores de envío de email
  115. $this->logger->error('Error al enviar email de recuperación: ' . $e->getMessage());
  116. $this->addFlash('error', 'No se pudo enviar el email de recuperación. Por favor, inténtelo más tarde.');
  117. return $this->render('home/authentication/recover/recover_request_email.html.twig');
  118. } catch (\Throwable $e) {
  119. throw $e;
  120. }
  121. }
  122. }