<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\ChangePasswordFormType;
use App\Form\ResetPasswordRequestFormType;
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\Mime\Email;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
use Psr\Log\LoggerInterface;
#[Route('/reset-password')]
class ResetPasswordController extends AbstractController
{
use ResetPasswordControllerTrait;
private ResetPasswordHelperInterface $resetPasswordHelper;
private EntityManagerInterface $entityManager;
private UrlGeneratorInterface $urlGenerator;
private LoggerInterface $logger;
public function __construct(
ResetPasswordHelperInterface $resetPasswordHelper,
EntityManagerInterface $entityManager,
UrlGeneratorInterface $urlGenerator,
LoggerInterface $logger
) {
$this->resetPasswordHelper = $resetPasswordHelper;
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->logger = $logger;
}
#[Route('', name: 'app_forgot_password_request')]
public function request(Request $request, MailerInterface $mailer, TranslatorInterface $translator): Response
{
$this->logger->info('Demande de réinitialisation de mot de passe');
// Vérifier si le formulaire a été soumis (méthode POST)
if ($request->isMethod('POST')) {
// Vérifier le reCAPTCHA
$recaptchaResponse = $request->request->get('g-recaptcha-response');
$url = 'https://www.google.com/recaptcha/api/siteverify';
$data = [
'secret' => '6LdsZb4qAAAAAI7xvU1kTognYncJkUCY35WpymUS',
'response' => $recaptchaResponse
];
$options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
$responseData = json_decode($result);
if ($responseData->success) {
// Le reCAPTCHA est valide, continuer avec l'envoi de l'email
$form = $this->createForm(ResetPasswordRequestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->logger->info('Formulaire de réinitialisation soumis');
return $this->processSendingPasswordResetEmail(
$form->get('email')->getData(),
$mailer,
$translator
);
}
} else {
// Le reCAPTCHA a échoué, affichez un message d'erreur
$this->addFlash('error', 'La vérification reCAPTCHA a échoué. Veuillez réessayer.');
return $this->redirectToRoute('app_check_email');
}
}
$form = $this->createForm(ResetPasswordRequestFormType::class);
return $this->render('reset_password/request.html.twig', [
'requestForm' => $form->createView(),
]);
}
#[Route('/check-email', name: 'app_check_email')]
public function checkEmail(): Response
{
$this->logger->info('Vérification de l\'email');
$resetToken = $this->getTokenObjectFromSession();
if (!$resetToken) {
$resetToken = $this->resetPasswordHelper->generateFakeResetToken();
}
return $this->render('reset_password/check_email.html.twig', [
'resetToken' => $resetToken,
]);
}
#[Route('/reset/{token}', name: 'app_reset_password')]
public function reset(Request $request, UserPasswordHasherInterface $userPasswordHasher, TranslatorInterface $translator, string $token = null): Response
{
$this->logger->info('Réinitialisation du mot de passe');
// Si un token est passé dans l'URL, stockez-le dans la session.
if ($token) {
$this->storeTokenInSession($token);
return $this->redirectToRoute('app_reset_password');
}
// Sinon, récupérez le token de la session.
$token = $this->getTokenFromSession();
if (null === $token) {
$this->logger->error('Aucun token de réinitialisation de mot de passe trouvé dans l\'URL ou dans la session.');
throw $this->createNotFoundException('Aucun token de réinitialisation de mot de passe trouvé dans l\'URL ou dans la session.');
}
// Validez le token et récupérez l'utilisateur associé.
try {
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
$this->logger->info('Token validé avec succès');
} catch (ResetPasswordExceptionInterface $e) {
$this->logger->error('Erreur lors de la validation du token: ' . $e->getMessage());
$this->addFlash('reset_password_error', sprintf(
'%s - %s',
$translator->trans(ResetPasswordExceptionInterface::MESSAGE_PROBLEM_VALIDATE, [], 'ResetPasswordBundle'),
$translator->trans($e->getReason(), [], 'ResetPasswordBundle')
));
return $this->redirectToRoute('app_forgot_password_request');
}
// Créez et gérez le formulaire de réinitialisation du mot de passe.
$form = $this->createForm(ChangePasswordFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->logger->info('Formulaire de réinitialisation soumis');
// Ne supprimez le token que si la réinitialisation du mot de passe est réussie.
$this->resetPasswordHelper->removeResetRequest($token);
// Hash le mot de passe et mettez à jour l'utilisateur.
$encodedPassword = $userPasswordHasher->hashPassword(
$user,
$form->get('plainPassword')->getData()
);
$user->setPassword($encodedPassword);
$this->entityManager->flush();
$this->logger->info('Mot de passe réinitialisé avec succès');
// Nettoyez la session après la réinitialisation.
$this->cleanSessionAfterReset();
return $this->redirectToRoute('app_login');
}
return $this->render('reset_password/reset.html.twig', [
'resetForm' => $form->createView(),
]);
}
private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer, TranslatorInterface $translator): RedirectResponse
{
try {
$this->logger->info('Début du processus de réinitialisation pour: ' . $emailFormData);
$user = $this->entityManager->getRepository(User::class)->findOneBy([
'email' => $emailFormData,
]);
if (!$user) {
$this->logger->warning('Utilisateur non trouvé: ' . $emailFormData);
return $this->redirectToRoute('app_check_email');
}
$this->logger->info('Utilisateur trouvé avec ID: ' . $user->getId());
try {
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
$this->logger->info('Token généré avec succès: ' . $resetToken->getToken());
} catch (ResetPasswordExceptionInterface $e) {
$this->logger->error('Erreur lors de la génération du token: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString()
]);
return $this->redirectToRoute('app_check_email');
}
$resetUrl = $this->urlGenerator->generate('app_reset_password', [
'token' => $resetToken->getToken()
], UrlGeneratorInterface::ABSOLUTE_URL);
$this->logger->info('URL de réinitialisation générée: ' . $resetUrl);
try {
$email = (new TemplatedEmail())
->from(new Address('no-reply@dimawork.fr', 'Dimawork'))
->to($user->getEmail())
->subject('Votre demande de réinitialisation de mot de passe')
->htmlTemplate('reset_password/email.html.twig')
->context([
'resetUrl' => $resetUrl,
'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime(),
'username' => $user->getPrenom(),
]);
$this->logger->info('Email préparé avec les paramètres suivants:', [
'from' => 'no-reply@dimawork.fr',
'to' => $user->getEmail(),
'resetUrl' => $resetUrl,
'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime()
]);
$mailer->send($email);
$this->logger->info('Email envoyé avec succès');
} catch (\Exception $e) {
$this->logger->error('Erreur lors de l\'envoi de l\'email: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString()
]);
throw $e;
}
$this->setTokenObjectInSession($resetToken);
return $this->redirectToRoute('app_check_email');
} catch (\Exception $e) {
$this->logger->error('Erreur générale dans processSendingPasswordResetEmail: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
}