src/Controller/SecurityController.php line 87

  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Symfony\Component\HttpFoundation\Response;
  5. use Symfony\Component\Routing\Annotation\Route;
  6. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  7. use App\Entity\User;
  8. use App\Entity\Settings;
  9. use App\Entity\ActiveSession;
  10. use App\Form\RegisterType;
  11. use Doctrine\Persistence\ManagerRegistry as PersistenceManagerRegistry;
  12. use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use App\Logger\DatabaseLogger;
  15. use App\Repository\UserRepository;
  16. use Symfony\Component\Mailer\MailerInterface;
  17. use Symfony\Component\Mime\Email;
  18. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  19. use Symfony\Bundle\SecurityBundle\Security;
  20. use \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  21. use App\Service\SessionControlService;
  22. use Symfony\Component\HttpFoundation\RequestStack;
  23. class SecurityController extends AbstractController {
  24.     public function __construct(
  25.         private UserPasswordHasherInterface $passwordEncoder,
  26.         private MailerInterface $mailer,
  27.         private ParameterBagInterface $params,
  28.         private Security $security,
  29.         private DatabaseLogger $logger,
  30.         private SessionControlService $sessionControlService,
  31.         private RequestStack $requestStack
  32.     ) {
  33.     }
  34.     #[Route(path'/security'name'security'methods: ['GET'])]
  35.     public function index(): Response {
  36.         return $this->render('security/index.html.twig', [
  37.             // 'controller_name' => 'SecurityController',
  38.         ]);
  39.     }
  40.     #[Route(path'/login'name'app_login')]
  41.     public function login(AuthenticationUtils $authenticationUtils): Response {
  42.         # https://auth0.com/blog/creating-your-first-symfony-app-and-adding-authentication/
  43.         // if ($this->getUser()) {
  44.         //     return $this->redirectToRoute('target_path');
  45.         // }
  46.         // get the login error if there is one
  47.         $error $authenticationUtils->getLastAuthenticationError();
  48.         // last username entered by the user
  49.         $lastUsername $authenticationUtils->getLastUsername();
  50.         return $this->render('security/login.html.twig', ['last_username' => $lastUsername'error' => $error]);
  51.     }
  52.     #[Route(path'/logout'name'app_pre_logout')]
  53.     public function pre_logout(Request $request): Response {
  54.         $user $this->security->getUser();
  55.         if (!$user) {
  56.             return $this->redirectToRoute('home');
  57.         }
  58.         $session = ($request->hasSession()) ? $request->getSession()->getId() : '';
  59.         $this->sessionControlService->registerActiveSessions($user->getEmail(), ActiveSession::STATUS_LOGGED_OUT_USER);
  60.         $this->logger->notice('USER LOGOUT');
  61.         return $this->security->logout(false);
  62.     }
  63.     #[Route(path'/real_logout'name'app_logout')]
  64.     public function logout(): void {
  65.         throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
  66.     }
  67.     #[Route(path'/register'name'register'methods: ['GET''POST'])]
  68.     public function register(Request $requestPersistenceManagerRegistry $doctrineDatabaseLogger $loggerTokenGeneratorInterface $tokenGeneratorUserRepository $userRepository) {
  69.         $em $doctrine->getManager();
  70.         $connection $doctrine->getConnection();
  71.         $settingsRepository $em->getRepository(Settings::class);
  72.         $regexObj $settingsRepository->findOneBy(['name' => 'regexpPass']);
  73.         $allowregister $settingsRepository->findOneByName('allowregister');
  74.         if (is_null($allowregister) || $allowregister->getValue() == 0) {
  75.             return $this->redirectToRoute('home');
  76.         }
  77.         $user = new User();
  78.         //TODO: para que haga algo parecido al "fill" de laravel tenemos que pasarle el entity
  79.         // $user en la function createForm.
  80.         $form $this->createForm(RegisterType::class, ['regexp' => $regexObj]);
  81.         $form->handleRequest($request);
  82.         if ($form->isSubmitted() && $form->isValid()) {
  83.             //comprobar que exista el correo sin validar y que tenga más de 15 min
  84.             // --> si existe el registro lo actualizamos
  85.             $user_check $em->getRepository(User::class)->createQueryBuilder('u')
  86.                 ->where('u.email = :email')
  87.                 ->setParameters([
  88.                     'email' => $request->get('register')['email'],
  89.                 ])
  90.                 ->getQuery()
  91.                 ->getOneOrNullResult();
  92.             if ($user_check) {
  93.                 if ($user_check->getConfirmed() === 1) {
  94.                     $this->addFlash('danger''Usuario ya registrado');
  95.                     return $this->redirectToRoute('register');
  96.                 }
  97.                 if ($user_check->getTimeCreated() >= new \DateTime('-15 minutes')) {
  98.                     $this->addFlash('danger''Revise su correo para completar el registro.');
  99.                     return $this->redirectToRoute('register');
  100.                 }
  101.                 $user $user_check;
  102.             }
  103.             // Encode the new users password
  104.             $token $tokenGenerator->generateToken();
  105.             $user->setConfirmationToken($token);
  106.             $user->setConfirmed(0);
  107.             $user->setStatus(User::STATUS_SUSPENDED);
  108.             $user->setPolicyAgreed($request->get('register')['policyagreed']);
  109.             $user->setEmail($request->get('register')['email']);
  110.             $user->setPassword($this->passwordEncoder->hashPassword($user$request->get('register')['password']['first']));
  111.             $user->setTimecreated(new \DateTime());
  112.             // Set their role
  113.             $user->setRoles(['ROLE_USER']);
  114.             // Save
  115.             $em $doctrine->getManager();
  116.             $em->persist($user);
  117.             $em->flush();
  118.             $logger->notice('Se ha creado un nuevo usuario.', [
  119.                 'data' => $user->serialize(),
  120.                 'userid' => $user->getId()
  121.             ]);
  122.             $this->sendConfirmationEmail($user);
  123.             $this->addFlash('success''Revise su correo para completar el registro.');
  124.             return $this->redirectToRoute('app_login');
  125.         }
  126.         return $this->render('security/register.html.twig', [
  127.             'form' => $form->createView(),
  128.         ]);
  129.     }
  130.     #[Route(path'/confirm-registration/{token}'name'confirm_registration'methods: ['GET'])]
  131.     public function confirmRegistration($tokenPersistenceManagerRegistry $doctrineDatabaseLogger $logger) {
  132.         $em $doctrine->getManager();
  133.         $user $em->getRepository(User::class)->findOneBy(['confirmationtoken' => $token'confirmed' => 0]);
  134.         $error 'Ocurrió un error al procesar la solicitud.';
  135.         if ($user) {
  136.             $error '';
  137.             $user->setConfirmed(1);
  138.             $user->setStatus(User::STATUS_ENABLED);
  139.             $em->persist($user);
  140.             $em->flush();
  141.             $logger->notice('Se ha confirmado el registro de un usuario.', [
  142.                 'data' => $user->serialize(),
  143.                 'userid' => $user->getId()
  144.             ]);
  145.         }
  146.         return $this->render('security/confirmation_result.html.twig', [
  147.             'error' => $error,
  148.         ]);
  149.     }
  150.     private function sendConfirmationEmail(User $user) {
  151.         try {
  152.             $email = new Email();
  153.             $email->from($_ENV['MAIL_SMTP_DO_NOT_REPLY_USERNAME'])
  154.                 ->to($user->getEmail())
  155.                 ->subject('Confirmación de registro')
  156.                 ->html(
  157.                     $this->renderView(
  158.                         'security/confirmation_email.html.twig',
  159.                         [
  160.                             'token' => $user->getConfirmationToken(),
  161.                             'user' => $user
  162.                         ]
  163.                     ),
  164.                     'text/html'
  165.                 );
  166.             $this->mailer->send($email);
  167.         } catch (\Exception $e) {
  168.             $this->logger->error(
  169.                 'Error avisando por correo a ' $user->getEmail(),
  170.                 [
  171.                     'userid' => $user->getId(),
  172.                     'data' => $e->getMessage(),
  173.                 ],
  174.             );
  175.         }
  176.     }
  177.     #[Route(path'/forgot-password'name'app_forgot_password_request')]
  178.     public function forgotPasswordRequest(Request $requestPersistenceManagerRegistry $doctrineDatabaseLogger $loggerTokenGeneratorInterface $tokenGenerator): Response {
  179.         $em $doctrine->getManager();
  180.         $regexp $em->getRepository(Settings::class)->findOneBy(['name' => 'regexpPass']);
  181.         if ($request->isMethod('POST')) {
  182.             $resetemail $request->get('resetemail');
  183.             $user $em->getRepository(User::class)->findOneBy(['email' => $resetemail'confirmed' => 1]);
  184.             if ($user) {
  185.                 $token $tokenGenerator->generateToken();
  186.                 $user->setConfirmationToken($token);
  187.                 $em->persist($user);
  188.                 $em->flush();
  189.                 try {
  190.                     $email = new Email();
  191.                     $email->from($_ENV['MAIL_SMTP_DO_NOT_REPLY_USERNAME'])
  192.                         ->to($user->getEmail())
  193.                         ->subject('Solicitud de reinicio de contraseña')
  194.                         ->html(
  195.                             $this->renderView(
  196.                                 'security/passwordreset_email.html.twig',
  197.                                 ['token' => $user->getConfirmationToken()]
  198.                             ),
  199.                             'text/html'
  200.                         );
  201.                     $this->mailer->send($email);
  202.                     $this->addFlash('success''Se ha enviado la solicitud de contraseña al correo indicado.');
  203.                     $logger->notice('Se ha solicitado un reinicio de contraseña para el usuario.', [
  204.                         'data' => $user->serialize(),
  205.                         'userid' => $user->getId()
  206.                     ]);
  207.                 } catch (\Exception $e) {
  208.                     $this->logger->error(
  209.                         'Error avisando por correo a ' $user->getEmail(),
  210.                         [
  211.                             'userid' => $user->getId(),
  212.                             'data' => $e->getMessage(),
  213.                         ],
  214.                     );
  215.                 }
  216.             }
  217.         }
  218.         return $this->render('security/passwordreset.html.twig', [
  219.             'confirmreset' => false,
  220.             'regexpPass' => $regexp,
  221.         ]);
  222.     }
  223.     #[Route(path'/reset-password/{token}'name'reset_password'methods: ['GET''POST'])]
  224.     public function resetPassword(Request $request$tokenPersistenceManagerRegistry $doctrineDatabaseLogger $loggerTokenGeneratorInterface $tokenGenerator) {
  225.         $em $doctrine->getManager();
  226.         $user $em->getRepository(User::class)->findOneBy(['confirmationtoken' => $token'confirmed' => 1]);
  227.         $regexp $em->getRepository(Settings::class)->findOneBy(['name' => 'regexpPass']);
  228.         if (!$user) {
  229.             $this->addFlash('danger''Esta página no está disponible.<br>Es posible que el enlace que seleccionaste no funcione o que se haya eliminado la página.');
  230.             return $this->redirectToRoute('home');
  231.         }
  232.         if ($request->isMethod('POST')) {
  233.             $password $request->get('password');
  234.             $user->setPassword($this->passwordEncoder->hashPassword($user$password));
  235.             $user->setConfirmationToken("");
  236.             $em->persist($user);
  237.             $em->flush();
  238.             $logger->notice('Se ha completado un reinicio de contraseña para el usuario.', [
  239.                 'data' => $user->serialize(),
  240.                 'userid' => $user->getId()
  241.             ]);
  242.             $this->addFlash('success''El password ha sido reseteado correctamente.');
  243.             return $this->redirectToRoute('home');
  244.         }
  245.         return $this->render('security/passwordreset.html.twig', [
  246.             'confirmreset' => true,
  247.             'regexpPass' => $regexp,
  248.         ]);
  249.     }
  250. }