<?php
namespace App\Controller;
use App\Entity\TbUsuarioHimedAdmin;
use App\Form\ChangePasswordFormType;
use App\Form\ResetPasswordRequestFormType;
use App\Repository\TbUsuarioHimedAdminRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
/**
* Controlador para el manejo del restablecimiento de contraseñas.
*
* @Route("/restablecer-contrasena")
*/
class ResetPasswordController extends AbstractController
{
use ResetPasswordControllerTrait;
private $em;
private $resetPasswordHelper;
private $tbUsuarioHimedAdminRepository;
public function __construct(ResetPasswordHelperInterface $resetPasswordHelper, TbUsuarioHimedAdminRepository $tbUsuarioHimedAdminRepository, EntityManagerInterface $em)
{
$this->resetPasswordHelper = $resetPasswordHelper;
$this->tbUsuarioHimedAdminRepository = $tbUsuarioHimedAdminRepository;
$this->em = $em;
}
/**
* Muestra y procesa el formulario para solicitar el restablecimiento de la contraseña.
*
* @Route("", name="app_forgot_password_request")
*/
public function request(Request $request, MailerInterface $mailer): Response
{
$form = $this->createForm(ResetPasswordRequestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->processSendingPasswordResetEmail(
$form->get('strCorreoElectronico')->getData(),
$mailer
);
}
return $this->render('reset_password/request.html.twig', [
'requestForm' => $form->createView(),
]);
}
/**
* Página de confirmación después de que un usuario haya solicitado el restablecimiento de su contraseña.
*
* @Route("/comprobar-correo", name="app_check_email")
*/
public function checkEmail(): Response
{
// Evitamos que los usuarios accedan directamente a esta página
if (empty($resetToken = $this->getTokenObjectFromSession())) {
return $this->redirectToRoute('app_forgot_password_request');
}
return $this->render('reset_password/check_email.html.twig', [
'resetToken' => $resetToken,
]);
}
/**
* Valida y procesa la URL de restablecimiento que el usuario ha pulsado en su correo electrónico.
*
* @Route("/restablecer/{token}", name="app_reset_password")
*/
public function reset(Request $request, UserPasswordHasherInterface $passwordEncoder, string $token = null): Response
{
if ($token) {
// Almacenamos el token en la sesión y lo eliminamos de la URL, para evitar que la URL sea
// cargada en un navegador y potencialmente filtrado el token a JavaScript de terceros.
$this->storeTokenInSession($token);
return $this->redirectToRoute('app_reset_password');
}
$token = $this->getTokenFromSession();
if (empty($token)) {
throw $this->createNotFoundException('No se ha encontrado ningún token de restablecimiento de contraseña en la URL o en la sesión.');
}
try {
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
} catch (ResetPasswordExceptionInterface $e) {
$this->addFlash('reset_password_error', sprintf(
'There was a problem validating your reset request - %s',
$e->getReason()
));
return $this->redirectToRoute('app_forgot_password_request');
}
// El token es válido; permite al usuario cambiar su contraseña.
$form = $this->createForm(ChangePasswordFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Un token de restablecimiento de contraseña debe usarse sólo una vez, elimínelo.
$this->resetPasswordHelper->removeResetRequest($token);
// Codifica la contraseña plana, y la establece.
$encodedPassword = $passwordEncoder->hashPassword(
$user,
$form->get('plainPassword')->getData()
);
$user->setStrContrasenia($encodedPassword);
$user->setIntIdUltimoUsuarioEditor($user);
$this->em->persist($user);
$this->em->flush();
// La sesión se limpia después de cambiar la contraseña.
$this->cleanSessionAfterReset();
$this->addFlash('success', '¡Restablecimiento exitoso!');
return $this->redirectToRoute('app_login');
}
return $this->render('reset_password/reset.html.twig', [
'resetForm' => $form->createView(),
]);
}
private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer): RedirectResponse
{
$user = $this->tbUsuarioHimedAdminRepository->findOneBy([
'strCorreoElectronico' => $emailFormData,
]);
// No revela si se encontró una cuenta de usuario o no.
if (!$user) {
return $this->redirectToRoute('app_check_email');
}
try {
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
} catch (ResetPasswordExceptionInterface $e) {
// Si desea indicar al usuario por qué no se ha enviado un correo electrónico de restablecimiento, descomente
// las líneas de abajo y cambie la redirección a 'app_forgot_password_request'.
// Precaución: Esto puede revelar si un usuario está registrado o no.
//
// $this->addFlash('reset_password_error', sprintf(
// 'Hubo un problema al validar su solicitud de restablecimiento - %s',
// $e->getReason()
// ));
return $this->redirectToRoute('app_check_email');
}
$email = (new TemplatedEmail())
->from(new Address('no_reply@himedsolutions.com', 'Administración HiMed Admin'))
->to($user->getStrCorreoElectronico())
->subject('Solicitud de nueva contraseña')
->htmlTemplate('reset_password/email.html.twig')
->context([
'resetToken' => $resetToken,
])
;
$mailer->send($email);
// Almacena el objeto token en la sesión para recuperarlo en la ruta de comprobación del correo electrónico.
$this->setTokenObjectInSession($resetToken);
return $this->redirectToRoute('app_check_email');
}
}