<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use LogicException;
use App\Entity\Usuario;
use App\Form\CambiarPasswordType;
use App\Form\FrontRegistroType;
use App\Service\Mails;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Contracts\Translation\TranslatorInterface;
class FrontSecurityController extends AbstractController
{
private $uploadsPath;
public function __construct(
private readonly EntityManagerInterface $em,
private readonly TranslatorInterface $translator,
private readonly Mails $mails,
private readonly UserPasswordHasherInterface $encoder,
private readonly ParameterBagInterface $params)
{
$this->uploadsPath = $this->params->get('uploadsPath').'/usuarios';
}
#[Route(path: '/login', name: 'front_app_login')]
public function login(AuthenticationUtils $authenticationUtils): RedirectResponse|Response
{
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirectToRoute('front_inicio');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('front-security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error
]);
}
#[Route(path: '/registro', name: 'front_registro', options: ['sitemap' => true])]
public function registro(Request $request): Response
{
$confirmarRegistro = $this->getParameter('confirmarRegistro');
$msgError = $this->translator->trans('notificaciones.operacion.error', [], 'app');
$usuario = new Usuario();
$form = $this->createForm(FrontRegistroType::class, $usuario);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
//Codificación de la contraseña
$passForm = $form->get('plainPassword')->getData();
if ($passForm) {
$pass = $this->encoder->hashPassword($usuario, $passForm);
$usuario->setPassword($pass);
}
$usuario->setActivo(!$confirmarRegistro);
$this->em->persist($usuario);
$this->em->flush();
// $emailsAdmins = $this->em->getRepository(Usuario::class)->obtenerEmailsAdmins();
// if (count($emailsAdmins) > 0) {
// $this->mails->sendNotification(
// "Nuevo proveedor registrado - {$proveedor->getNombreFiscal()}",
// "<p>Se acaba de registrar un nuevo proveedor.</p>
// <br>
// <p>Estos son los detalles:</p>
// <p><strong>Proveedor:</strong> {$proveedor->getNombreFiscal()}</p>
// <p><strong>Email:</strong> {$proveedor->getEmail()}</p>
// <p><strong>Persona contacto:</strong> {$proveedor->getPersonaContacto()}</p>
// <p><strong>Teléfono:</strong> {$proveedor->getTelefonoPersonaContacto()}</p>",
// $emailsAdmins,
// null,
// true
// );
// }
if ($confirmarRegistro) {
$usuario->setTokenConfirmacion(sha1(uniqid(mt_rand(), true)));
$urlConfirmacionRegistro = $this->generateUrl('front_confirmar_registro', ['tokenConfirmacion' => $usuario->getTokenConfirmacion()], UrlGeneratorInterface::ABSOLUTE_URL);
$this->mails->sendNotification(
'Confirmación de su registro',
"<p>Hola ".$usuario->getUsuario().": </p>
<p>Para acceder al sistema es necesario confirmar su registro. Para confirmarlo acceda <u><a class='btn btn-sm btn-primary' href='".$urlConfirmacionRegistro."'>aquí</a></u>.</p>",
$usuario->getEmail(),
null,
true
);
$this->em->flush();
}
return $this->render('front-security/registro-finalizado.html.twig');
} catch (Exception $e) {
$this->addFlash('error', $msgError);
}
}
return $this->render('front-security/registro.html.twig', [
'usuario' => $usuario,
'form' => $form->createView(),
'error' => $msgError
]);
}
#[Route(path: '/cambiar-contrasena', name: 'front_cambiar_contrasena')]
public function cambiarContrasena(Request $request): Response
{
$msgError = $this->translator->trans('notificaciones.operacion.error', [], 'app');
$form = $this->createFormBuilder()
->add('usuarioEmail', TextType::class, [
'label' => false,
'attr' => [
'class' => 'manual-placeholder',
'autocomplete' => 'Off',
'placeholder' => 'Introduzca nombre de usuario o email'
],
'constraints' => [
new NotBlank(),
new Length([
'max' => 100
])
]
])
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
$usuarioEmailForm = $form->get('usuarioEmail')->getData();
// Buscamos el usuario introducido y validamos que exista y esté activo
$usuario = $this->em->getRepository(Usuario::class)
->createQueryBuilder('u')
->andWhere('u.activo = true AND u.borrado = false AND (u.usuario = :u OR u.email = :u)')
->setParameters([
':u' => $usuarioEmailForm
])
->setMaxResults(1)
->getQuery()
->getResult();
$usuario = count($usuario) > 0 ? $usuario[0] : null;
if(!$usuario){
$msgError = 'El nombre de usuario o email indicado no existe.';
throw new Exception();
}
if(!$usuario->getEmail()){
$msgError = 'No ha sido posible restablecer la contraseña ya que el usuario no tiene un email asociado. Contacte con un responsable.';
throw new Exception();
}
if ($usuario->getFechaSolicitudPassword() !== null) {
$now = new DateTime();
$fechaSolicitudPassword = $usuario->getFechaSolicitudPassword();
$minutos = abs($now->getTimestamp() - $fechaSolicitudPassword->getTimestamp()) / 60 ;
// Si no han pasado dos horas entre solicitud y solicitud, no hacemos nada
if ($minutos < 120) {
$msgError = 'Debe esperar dos horas para poder volver a solicitar un cambio de contraseña.';
throw new Exception();
}
}
$usuario
->setFechaSolicitudPassword(new DateTime())
->setTokenConfirmacion(sha1(uniqid(mt_rand(), true)));
$urlCambiarPassword = $this->generateUrl('front_confirmar_cambio_contrasena', ['tokenConfirmacion' => $usuario->getTokenConfirmacion()], UrlGeneratorInterface::ABSOLUTE_URL);
$this->mails->sendNotification(
'Solicitud de nueva contraseña',
"<p>Hola ".$usuario->getUsername().": </p>
<p>Para restablecer tu contraseña - por favor pincha <u><a class='btn btn-sm btn-primary' href='".$urlCambiarPassword."'>aquí</a></u>.</p>",
$usuario->getEmail(),
null,
true
);
$this->em->flush();
return $this->render('front-security/comprobar-email-cambiar-contrasena.html.twig');
} catch (Exception) {
$this->addFlash('error', $msgError);
}
}
return $this->render('front-security/cambiar-contrasena.html.twig', [
'form' => $form->createView(),
'error' => $msgError
]);
}
#[Route(path: '/confirmar-cambio-contrasena/{tokenConfirmacion}', name: 'front_confirmar_cambio_contrasena')]
public function confirmarCambioContrasena(Request $request, Usuario $usuario = null): RedirectResponse|Response
{
if (!$usuario instanceof Usuario) {
return $this->redirectToRoute('front_app_login');
}
if ($usuario->getFechaSolicitudPassword() < (new DateTime())->modify('-1 days')) {
$this->addFlash('error', 'El enlace a caducado. Debe solicitar nuevamente la renovación de contraseña.');
return $this->redirectToRoute('front_app_login');
}
$this->translator->trans('notificaciones.operacion.ok', [], 'app');
$msgError = $this->translator->trans('notificaciones.operacion.error', [], 'app');
$form = $this->createForm(CambiarPasswordType::class, $usuario);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
//Codificación de la contraseña
$passForm = $form->get('password')->getData();
if ($passForm) {
$pass = $this->encoder->hashPassword($usuario, $passForm);
$usuario
->setPassword($pass)
->setTokenConfirmacion(null)
->setFechaSolicitudPassword(null);
}
$this->em->flush();
return $this->render('front-security/contrasena-cambiada.html.twig');
} catch (Exception) {
$this->addFlash('error', $msgError);
}
}
return $this->render('front-security/confirmar-cambio-contrasena.html.twig', [
'form' => $form->createView(),
'error' => $msgError
]);
}
#[Route(path: '/confirmar-registro/{tokenConfirmacion}', name: 'front_confirmar_registro')]
public function confirmarRegistro(Usuario $usuario = null): RedirectResponse|Response
{
if (!$usuario instanceof Usuario) {
return $this->redirectToRoute('front_app_login');
}
$usuario
->setActivo(true)
->setTokenConfirmacion(null);
$this->em->flush();
return $this->render('front-security/registro-confirmado.html.twig');
}
#[Route(path: '/logout', name: 'front_app_logout')]
public function logout(): never
{
throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}