<?php
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class SecurityController extends AbstractController
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* @Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils, Request $request): Response
{
$this->logger->info('Login attempt', ['method' => $request->getMethod()]);
if ($this->getUser()) {
$this->logger->info('User already logged in, redirecting to home');
return $this->redirectToRoute('app_home');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
if ($error) {
$this->logger->warning('Login error', ['error' => $error->getMessage()]);
}
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* @Route("/logout", name="app_logout")
*/
public function logout(): void
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
/**
* @Route("/password-reset", name="app_password_reset")
*/
public function resetPassword(Request $request, MailerInterface $mailer)
{
$email = $request->query->get('email');
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(['email' => $email]);
if (!$user) {
$this->logger->info('Password reset attempt for unknown email', ['email' => $email]);
return $this->redirectToRoute('app_login', ['error' => 'Email inconnu']);
}
$token = bin2hex(random_bytes(32));
$user->setResetToken($token);
$user->setResetTokenExpiresAt(new \DateTime('+1 hour'));
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$resetUrl = $this->generateUrl('app_reset_password', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL);
$mail = (new Email())
->from('contact@flexenergie.fr')
->to($email)
->subject('Réinitialisation de mot de passe')
->html($this->renderView(
'email/reset_password.html.twig',
['resetUrl' => $resetUrl]
));
try {
$mailer->send($mail);
$this->logger->info('Password reset email sent', ['email' => $email]);
$this->addFlash('success', 'Un e-mail de réinitialisation a été envoyé à votre adresse.');
} catch (TransportExceptionInterface $e) {
$this->logger->error('Failed to send password reset email', ['email' => $email, 'error' => $e->getMessage()]);
$this->addFlash('error', 'Une erreur est survenue lors de l\'envoi de l\'e-mail. Veuillez réessayer plus tard.');
}
return $this->redirectToRoute('app_login');
}
/**
* @Route("/reset-password/{token}", name="app_reset_password")
*/
public function resetPasswordForm(Request $request, string $token, UserPasswordEncoderInterface $passwordEncoder)
{
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(['resetToken' => $token]);
if (!$user || $user->getResetTokenExpiresAt() < new \DateTime()) {
$this->addFlash('reset_password_error', 'Le lien de réinitialisation est invalide ou a expiré.');
return $this->redirectToRoute('app_login');
}
$form = $this->createFormBuilder()
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'first_options' => [
'constraints' => [
new NotBlank([
'message' => 'Veuillez entrer un mot de passe',
]),
new Length([
'min' => 6,
'minMessage' => 'Votre mot de passe doit comporter au moins {{ limit }} caractères',
'max' => 4096,
]),
],
'label' => 'Nouveau mot de passe',
],
'second_options' => [
'label' => 'Répéter le mot de passe',
],
'invalid_message' => 'Les champs du mot de passe doivent correspondre.',
'mapped' => false,
])
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$newPassword = $form->get('plainPassword')->getData();
$user->setPassword($passwordEncoder->encodePassword($user, $newPassword));
$user->setResetToken(null);
$user->setResetTokenExpiresAt(null);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash('success', 'Votre mot de passe a été mis à jour avec succès.');
return $this->redirectToRoute('app_login');
}
return $this->render('security/reset_password.html.twig', [
'resetForm' => $form->createView(),
]);
}
/**
* @Route("/password-change", name="app_password_change")
*/
public function changePassword()
{
return $this->render('security/change_password.html.twig');
}
/**
* @Route("/password-update", name="app_password_update")
*/
public function updatePassword(Request $request, UserPasswordEncoderInterface $passwordEncoder, UserInterface $user)
{
$new_password = $request->request->get('new_password');
$user->setPassword($passwordEncoder->encodePassword($user, $new_password));
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash('success', 'Mot de passe mis à jour avec succès.');
return $this->redirectToRoute('app_login');
}
}