<?php
namespace App\EventSubscriber;
use App\Entity\SsoUser;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Workflow\Registry;
class LoginAttemptSubscriber implements EventSubscriberInterface
{
private $requestStack;
private $workflows;
private $em;
private const MAX_ATTEMPTS = 3;
public function __construct(RequestStack $requestStack, Registry $workflows, EntityManagerInterface $em)
{
$this->requestStack = $requestStack;
$this->workflows = $workflows;
$this->em = $em;
}
public static function getSubscribedEvents(): array
{
return [
// En el sistema Guard (legacy), este es el evento de fallo
'security.authentication.failure' => 'onAuthenticationFailure',
// Este es el evento de éxito
'security.interactive_login' => 'onLoginSuccess',
];
}
public function onAuthenticationFailure(AuthenticationFailureEvent $event): void
{
$session = $this->requestStack->getSession();
$attempts = $session->get('login_attempts', 0) + 1;
$session->set('login_attempts', $attempts);
if ($attempts >= self::MAX_ATTEMPTS) {
// Como el Token está vacío, le pedimos el dato a la Request actual
$request = $this->requestStack->getCurrentRequest();
// Obtenemos el campo 'username' que viene del formulario POST
$username = $request->request->get('username');
if ($username) {
$user = $this->em->getRepository(SsoUser::class)->findOneBy([
'username' => $username
]);
if ($user instanceof SsoUser) {
$workflow = $this->workflows->get($user, 'usuario');
if ($workflow->can($user, 'bloquear')) {
$workflow->apply($user, 'bloquear');
$this->em->flush();
}
}
}
}
}
public function onLoginSuccess(InteractiveLoginEvent $event): void
{
// Si el login es correcto, reseteamos el contador de la sesión
$this->requestStack->getSession()->remove('login_attempts');
}
}