src/Controller/SecurityController.php line 150

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\User;
  4. use App\Form\UserType;
  5. use App\Security\AppAuthenticator;
  6. use Doctrine\ORM\EntityManagerInterface;
  7. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpFoundation\Response;
  10. use Symfony\Component\Routing\Annotation\Route;
  11. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  12. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  13. use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
  14. class SecurityController extends AbstractController
  15. {
  16.     #[Route('/connexion'name'app_login'methods: ['GET''POST'])]
  17.     public function login(AuthenticationUtils $authenticationUtilsRequest $request): Response
  18.     {
  19.         if ($this->getUser()) {
  20.             // Gérer la redirection après connexion si un utilisateur est déjà connecté
  21.             $redirect $request->query->get('redirect');
  22.             if ($redirect && $this->isUrlSafe($redirect)) {
  23.                 return $this->redirect($redirect);
  24.             }
  25.             
  26.             if ($this->isGranted('ROLE_ADMIN')) {
  27.                 return $this->redirectToRoute('app_dashboard');
  28.             }
  29.             return $this->redirectToRoute('app_candidature_index');
  30.         }
  31.         $error $authenticationUtils->getLastAuthenticationError();
  32.         $lastUsername $authenticationUtils->getLastUsername();
  33.         
  34.         // Récupérer l'URL de redirection
  35.         $redirect $request->query->get('redirect');
  36.         return $this->render('security/login.html.twig', [
  37.             'last_username' => $lastUsername,
  38.             'error' => $error,
  39.             'redirect' => $redirect
  40.         ]);
  41.     }
  42.     #[Route('/mot-de-passe-oublie'name'app_forgot_password'methods: ['GET''POST'])]
  43.     public function forgotPassword(Request $requestEntityManagerInterface $entityManagerUserPasswordHasherInterface $passwordHasher): Response
  44.     {
  45.         $step $request->getSession()->get('forgot_password_step'1);
  46.         $userId $request->getSession()->get('forgot_password_user_id');
  47.         $identifier '';
  48.         
  49.         // Étape 1 : Vérification de l'utilisateur
  50.         if ($request->isMethod('POST') && $request->request->has('verify_user')) {
  51.             $identifier trim($request->request->get('identifier'''));
  52.             $lastFourDigits trim($request->request->get('last_four_digits'''));
  53.             
  54.             if (empty($identifier) || empty($lastFourDigits)) {
  55.                 $this->addFlash('error''Veuillez remplir tous les champs.');
  56.                 return $this->render('security/forgot_password.html.twig', ['step' => 1]);
  57.             }
  58.             
  59.             // Rechercher l'utilisateur par email ou par contact
  60.             $user $entityManager->getRepository(User::class)->findOneBy(['email' => $identifier]);
  61.             if (!$user && str_starts_with($identifier'0') && strlen($identifier) >= 8) {
  62.                 $user $entityManager->getRepository(User::class)->findOneBy(['contact' => $identifier]);
  63.             }
  64.             
  65.             if (!$user) {
  66.                 $this->addFlash('error''Aucun utilisateur trouvé avec cet identifiant.');
  67.                 return $this->render('security/forgot_password.html.twig', ['step' => 1]);
  68.             }
  69.             
  70.             // Vérifier les 4 derniers chiffres du numéro de téléphone
  71.             $contact $user->getContact();
  72.             if (!$contact || strlen($contact) < 4) {
  73.                 $this->addFlash('error''Aucun numéro de téléphone associé à ce compte.');
  74.                 return $this->render('security/forgot_password.html.twig', ['step' => 1]);
  75.             }
  76.             
  77.             $lastFour substr(str_replace(' '''$contact), -4);
  78.             if ($lastFour !== $lastFourDigits) {
  79.                 $this->addFlash('error''Les 4 derniers chiffres du numéro de téléphone ne correspondent pas.');
  80.                 return $this->render('security/forgot_password.html.twig', ['step' => 1]);
  81.             }
  82.             
  83.             // Stocker l'ID utilisateur en session
  84.             $request->getSession()->set('forgot_password_user_id'$user->getId());
  85.             $request->getSession()->set('forgot_password_step'2);
  86.             
  87.             return $this->render('security/forgot_password.html.twig', ['step' => 2'identifier' => $identifier]);
  88.         }
  89.         
  90.         // Étape 2 : Nouveau mot de passe
  91.         if ($request->isMethod('POST') && $request->request->has('reset_password')) {
  92.             if (!$userId) {
  93.                 $this->addFlash('error''Session expirée. Veuillez recommencer.');
  94.                 return $this->redirectToRoute('app_forgot_password');
  95.             }
  96.             
  97.             $user $entityManager->getRepository(User::class)->find($userId);
  98.             if (!$user) {
  99.                 $this->addFlash('error''Utilisateur introuvable.');
  100.                 return $this->redirectToRoute('app_forgot_password');
  101.             }
  102.             
  103.             // Récupérer l'identifiant pour l'affichage
  104.             $identifier $user->getEmail() ?: $user->getContact();
  105.             
  106.             $newPassword $request->request->get('new_password''');
  107.             $confirmPassword $request->request->get('confirm_password''');
  108.             
  109.             if (empty($newPassword) || empty($confirmPassword)) {
  110.                 $this->addFlash('error''Veuillez remplir tous les champs.');
  111.                 return $this->render('security/forgot_password.html.twig', ['step' => 2'identifier' => $identifier]);
  112.             }
  113.             
  114.             if ($newPassword !== $confirmPassword) {
  115.                 $this->addFlash('error''Les mots de passe ne correspondent pas.');
  116.                 return $this->render('security/forgot_password.html.twig', ['step' => 2'identifier' => $identifier]);
  117.             }
  118.             
  119.             if (strlen($newPassword) < 6) {
  120.                 $this->addFlash('error''Le mot de passe doit contenir au moins 6 caractères.');
  121.                 return $this->render('security/forgot_password.html.twig', ['step' => 2'identifier' => $identifier]);
  122.             }
  123.             
  124.             // Hachage et mise à jour du mot de passe
  125.             $user->setPassword($passwordHasher->hashPassword($user$newPassword));
  126.             $entityManager->flush();
  127.             
  128.             // Nettoyer la session
  129.             $request->getSession()->remove('forgot_password_step');
  130.             $request->getSession()->remove('forgot_password_user_id');
  131.             
  132.             $this->addFlash('success''Votre mot de passe a été réinitialisé avec succès. Veuillez vous connecter.');
  133.             return $this->redirectToRoute('app_login');
  134.         }
  135.         
  136.         // Réinitialiser la session si on arrive sur la page
  137.         $request->getSession()->remove('forgot_password_step');
  138.         $request->getSession()->remove('forgot_password_user_id');
  139.         
  140.         return $this->render('security/forgot_password.html.twig', ['step' => 1]);
  141.     }
  142.     #[Route('/inscription'name'app_register'methods: ['GET''POST'])]
  143.     public function register(
  144.         Request $request,
  145.         UserPasswordHasherInterface $passwordHasher,
  146.         EntityManagerInterface $entityManager,
  147.         UserAuthenticatorInterface $authenticator,
  148.         AppAuthenticator $formAuthenticator
  149.     ): Response {
  150.         if ($this->getUser()) {
  151.             // Gérer la redirection après inscription si un utilisateur est déjà connecté
  152.             $redirect $request->query->get('redirect');
  153.             if ($redirect && $this->isUrlSafe($redirect)) {
  154.                 return $this->redirect($redirect);
  155.             }
  156.             return $this->redirectToRoute('app_candidature_index');
  157.         }
  158.         $user = new User();
  159.         $form $this->createForm(UserType::class, $user, [
  160.             'is_register' => true,
  161.             'is_edit' => false
  162.         ]);
  163.         $form->handleRequest($request);
  164.         if ($form->isSubmitted() && $form->isValid()) {
  165.             // Vérifier si l'email existe déjà
  166.             $existingUser $entityManager->getRepository(User::class)->findOneBy(['email' => $user->getEmail()]);
  167.             if ($existingUser) {
  168.                 $this->addFlash('error''Cet email est déjà utilisé.');
  169.                 return $this->renderForm('security/register.html.twig', [
  170.                     'form' => $form,
  171.                     'redirect' => $request->query->get('redirect')
  172.                 ]);
  173.             }
  174.             // Hachage du mot de passe
  175.             $user->setPassword(
  176.                 $passwordHasher->hashPassword($user$form->get('password')->getData())
  177.             );
  178.             
  179.             // Attribution du rôle candidat
  180.             $user->setRoles(['ROLE_CANDIDAT']);
  181.             $entityManager->persist($user);
  182.             $entityManager->flush();
  183.             // Authentification automatique
  184.             $authenticator->authenticateUser($user$formAuthenticator$request);
  185.             $this->addFlash('success''Votre compte a été créé avec succès.');
  186.             
  187.             // Redirection après inscription
  188.             $redirect $request->query->get('redirect');
  189.             if ($redirect && $this->isUrlSafe($redirect)) {
  190.                 return $this->redirect($redirect);
  191.             }
  192.             
  193.             return $this->redirectToRoute('app_candidature_new');
  194.         }
  195.         return $this->renderForm('security/register.html.twig', [
  196.             'form' => $form,
  197.             'redirect' => $request->query->get('redirect')
  198.         ]);
  199.     }
  200.     #[Route('/deconnexion'name'app_logout')]
  201.     public function logout(): void
  202.     {
  203.         throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
  204.     }
  205.     /**
  206.      * Vérifie si une URL de redirection est sûre (évite les redirections vers des domaines externes)
  207.      */
  208.     private function isUrlSafe(string $url): bool
  209.     {
  210.         // Vérifier que l'URL commence par / (chemin relatif)
  211.         if (str_starts_with($url'/')) {
  212.             return true;
  213.         }
  214.         
  215.         // Vérifier que l'URL est sur le même domaine
  216.         $parsedUrl parse_url($url);
  217.         if (isset($parsedUrl['host'])) {
  218.             $request Request::createFromGlobals();
  219.             return $parsedUrl['host'] === $request->getHost();
  220.         }
  221.         
  222.         return false;
  223.     }
  224. }