src/Controller/SecurityController.php line 152

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Letter;
  4. use App\Entity\Organization;
  5. use App\Entity\ResetPasswordToken;
  6. use App\Entity\User;
  7. use App\Entity\UserProfile;
  8. use App\Exception\UnauthorizedRegistrationException;
  9. use App\Exception\UnmatchedDomainException;
  10. use App\Form\User\AccountCreationFormType;
  11. use App\Form\User\PasswordFormType;
  12. use App\Repository\UserRepository;
  13. use App\Service\Mailer\SattMailer;
  14. use App\Service\Notifier\SattNotificationService;
  15. use App\Service\UserRegisterService;
  16. use Doctrine\ORM\EntityManagerInterface;
  17. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  18. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  19. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Response;
  22. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  23. use Symfony\Component\Routing\Annotation\Route;
  24. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  25. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  26. use Symfony\Contracts\Translation\TranslatorInterface;
  27. use App\Service\TwoFactorAuthService;
  28. use Symfony\Component\Security\Core\Security;
  29. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  30. /**
  31.  * Class SecurityController.
  32.  */
  33. class SecurityController extends AbstractController
  34. {
  35.     private $sattMailer;
  36.     private UserPasswordHasherInterface $passwordHasher;
  37.     private $sattNotification;
  38.     private EntityManagerInterface $em;
  39.     private TwoFactorAuthService $twoFactorAuthService;
  40.     public function __construct(
  41.         SattMailer $sattMailer,
  42.         UserPasswordHasherInterface $passwordHasher,
  43.         SattNotificationService $sattNotification,
  44.         EntityManagerInterface $entityManager,
  45.         TwoFactorAuthService $twoFactorAuthService,
  46.     ) {
  47.         $this->sattMailer $sattMailer;
  48.         $this->passwordHasher $passwordHasher;
  49.         $this->sattNotification $sattNotification;
  50.         $this->em $entityManager;
  51.         $this->twoFactorAuthService $twoFactorAuthService;
  52.     }
  53.     #[Route(path'/'name'app_login'options: ['expose' => true])]
  54.     public function login(AuthenticationUtils $authenticationUtils): Response
  55.     {
  56.         // if ($this->getUser()) {
  57.         //     return $this->redirectToRoute('target_path');
  58.         // }
  59.         // get the login error if there is one
  60.         $error $authenticationUtils->getLastAuthenticationError();
  61.         // last username entered by the user
  62.         $lastUsername $authenticationUtils->getLastUsername();
  63.         return $this->render('security/login.html.twig', ['last_username' => $lastUsername'error' => $error]);
  64.     }
  65.     #[Route(path'/logout'name'app_logout')]
  66.     public function logout()
  67.     {
  68.         throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
  69.     }
  70.     #[Route(path'/create'name'create_account')]
  71.     public function createAccount(
  72.         Request $request,
  73.         UserRepository $userRepository,
  74.         UserRegisterService $userRegisterService,
  75.         TranslatorInterface $translator
  76.     ) {
  77.         $form $this->createForm(AccountCreationFormType::class);
  78.         $form->handleRequest($request);
  79.         if ($form->isSubmitted() && $form->isValid()) {
  80.             /** @var User $userForm */
  81.             $userForm $form->getData();
  82.             $isFormFlawed false;
  83.             if ($userRepository->findOneByAttribute($form['email']->getData(), 'email')) {
  84.                 $this->addFlash('error''flash.email_taken');
  85.                 $isFormFlawed true;
  86.             }
  87.             // Statut is required by the form, no need to check if null
  88.             if (!$userForm->getOrganization() || Organization::TYPE_UNIVERSITY !== $userForm->getOrganization()->getType()) {
  89.                 if ($userForm->getStatut()->getIsUniversityRequired()) {
  90.                     $this->addFlash('error''flash.statut_require_university');
  91.                     $isFormFlawed true;
  92.                 }
  93.             }
  94.             /* // Handle usernames
  95.             if($this->getDoctrine()->getRepository(User::class)->findOneByAttribute($form['username']->getData(), 'username')){
  96.                 //$this->addFlash('error', 'flash.username_taken');
  97.                 $isFormFlawed = true;
  98.             } */
  99.             if ($isFormFlawed) {
  100.                 return $this->render('security/create_account.html.twig', ['form' => $form->createView()]);
  101.             }
  102.             try {
  103.                 $user = new User();
  104.     
  105.                 $user->setEmail($userForm->getEmail())
  106.                     ->setFirstName($userForm->getFirstName())
  107.                     ->setLastName($userForm->getLastName())
  108.                     ->setStatut($userForm->getStatut())
  109.                     ->setOrganization($userForm->getOrganization())
  110.                     ->setPassword($this->passwordHasher->hashPassword($user$form['password']->getData()))
  111.                     ->setUserProfile((new UserProfile()))
  112.                     ->setUsername('user_'.uniqid());
  113.     
  114.                 $entityManager $this->em;
  115.                 $userRegisterService->register($user);
  116.                 $entityManager->persist($user);
  117.     
  118.                 $entityManager->flush();
  119.     
  120.                 $this->addFLash('success''flash.creation_success');
  121.             } catch (UnmatchedDomainException $e) {
  122.                 $this->addFLash('error'$translator->trans('flash.register.unmatched_domain_error', ['%domain%' => $e->getMessage()]));
  123.             } catch (UnauthorizedRegistrationException $e) {
  124.                 $this->addFLash('error''flash.register.creation_error');
  125.             }
  126.             return $this->redirectToRoute('app_login');
  127.         }
  128.         return $this->render(
  129.             'security/create_account.html.twig',
  130.             ['form' => $form->createView()]
  131.         );
  132.     }
  133.     #[Route(path'/change-password'name'change_password')]
  134.     public function forgottenPassword(Request $requestUserRepository $userRepository)
  135.     {
  136.         $form $this->createFormBuilder()
  137.         ->add('email'EmailType::class, ['label' => 'form.label.email'])
  138.         ->add('submit'SubmitType::class, ['label' => 'form.action.reinitialized'])
  139.         ->getForm();
  140.         $form->handleRequest($request);
  141.         if ($form->isSubmitted() && $form->isValid()) {
  142.             /** @var User $user */
  143.             if ($user $userRepository->findOneByAttribute($form['email']->getData(), 'email')) {
  144.                 if (!($token $user->getResetPasswordToken())) { // No token associated to User
  145.                     $token = new ResetPasswordToken();
  146.                 } /* else {    // Do not resend a token if the user has a valid reset password token
  147.                     if ($user->getResetPasswordToken()->isItExpired(time(), $this->getParameter('RESET_PASSWORD_TOKEN_EXPIRATION_DELAY'))) {
  148.                         return $this->redirectToRoute('app_login');
  149.                     }
  150.                 } */ else {
  151.                     $token $user->getResetPasswordToken();
  152.                 }
  153.                 $entityManager $this->em;
  154.                 // Token value
  155.                 $tokenValue rtrim(strtr(base64_encode(random_bytes(64)), '+/''-_'), '=');
  156.                 $token
  157.                     ->setValue($tokenValue)
  158.                     ->setExpirationDate(time() + $this->getParameter('RESET_PASSWORD_TOKEN_EXPIRATION_DELAY'));
  159.                 $user->setResetPasswordToken($token);
  160.                 $entityManager->persist($token);
  161.                 $entityManager->persist($user);
  162.                 $entityManager->flush();
  163.                 $url $this->generateUrl(
  164.                     'reset_password'
  165.                     ['token' => $token->getValue()], 
  166.                     UrlGeneratorInterface::ABSOLUTE_URL
  167.                 );
  168.                 $this->sattMailer->sendComputedEmailByCode(
  169.                     [$form['email']->getData()],
  170.                     Letter::CODE['USER_RESET_PASSWORD'],
  171.                     [],
  172.                     array_merge($user->getBindings(), [
  173.                         'USER.RESET.PASSWORD' => $url
  174.                     ])
  175.                 );
  176.                 
  177.                 $this->addFlash('success''flash.reset_password');
  178.             }
  179.             return $this->redirectToRoute('change_password');
  180.         }
  181.         return $this->render(
  182.             'security/change_password.html.twig',
  183.             ['form' => $form->createView(), 'error' => null]
  184.         );
  185.     }
  186.     /**
  187.      * Handle password reset with tokens sent by mail.
  188.      */
  189.     #[Route(path'/reset-password-token'name'reset_password')]
  190.     public function resetPassword(Request $requestUserRepository $userRepository)
  191.     {
  192.         if (!$request->query->get('token')) {
  193.             return $this->redirectToRoute('app_login');
  194.         }
  195.         $tokenValue $request->query->get('token');
  196.         /** @var User $tuser */
  197.         $user $userRepository->findOneByResetToken($tokenValue);
  198.         //verify if token exists/is expired
  199.         if (!$user || $user->getResetPasswordToken()->isItExpired(time(), $this->getParameter('RESET_PASSWORD_TOKEN_EXPIRATION_DELAY'))) {
  200.             $this->addFlash('error''flash.user_profile.invalid_token');
  201.             return $this->redirectToRoute('app_login');
  202.         }
  203.         $form $this->createForm(PasswordFormType::class);
  204.         $form->handleRequest($request);
  205.         if ($form->isSubmitted() && $form->isValid()) {
  206.             $entityManager $this->em;
  207.             $user->setPassword($this->passwordHasher->hashPassword($user$form['newPassword']->getData()));
  208.             $token $user->getResetPasswordToken();
  209.             $entityManager->persist($user);
  210.             $entityManager->remove($token);
  211.             $entityManager->flush();
  212.             $this->addFLash('success''flash.user_profile.password_reset_success');
  213.             return $this->redirectToRoute('app_login');
  214.         }
  215.         return $this->render(
  216.             'security/change_password_form.html.twig',
  217.             ['form' => $form->createView()]
  218.         );
  219.     }
  220.     #[Route('/2fa'name'app_2fa')]
  221.     public function twoFactor(Request $requestSecurity $securityTwoFactorAuthService $twoFactorAuthServiceSessionInterface $session): Response
  222.     {
  223.         /** @var User $user */
  224.         $user $security->getUser();
  225.         if (!$user) {
  226.             return $this->redirectToRoute('app_login');
  227.         }
  228.         // si 2FA désactivée ou déjà validée → on rebondit
  229.         if (!$user->isTwoFactorEnabled() || $session->get('2fa_verified'false)) {
  230.             $route \array_intersect(['ROLE_ADMIN','ROLE_SCIENTIFIC_ADMIN'], $user->getRoles()) ? 'admin' 'dashboard';
  231.             return $this->redirectToRoute($route);
  232.         }
  233.         if ($request->isMethod('POST')) {
  234.             $code $request->request->get('code');
  235.             $expiresAt $user->getTwoFactorExpiresAt();
  236.             if ($user->getTwoFactorCode() === $code && $expiresAt > new \DateTime()) {
  237.                 $session->set('2fa_verified'true);
  238.                 $twoFactorAuthService->clear($user);
  239.                 if (in_array('ROLE_ADMIN'$user->getRoles(), true)) {
  240.                     return $this->redirectToRoute('admin'); 
  241.                 }
  242.                 return $this->redirectToRoute('dashboard'); 
  243.             }
  244.             $this->addFlash('danger''Code invalide ou expiré');
  245.         }
  246.         return $this->render('security/2fa.html.twig');
  247.     }
  248. }