<?php
namespace AppBundle\Security;
use AppBundle\Entity\AccessLog;
use AppBundle\Entity\Customer;
use AppBundle\Repository\AccessLogRepository;
use AppBundle\Repository\CustomerRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
class LoginLogServiceEventSubscriber implements EventSubscriberInterface
{
const TO_MANY_FAILURES = 5;
public function __construct(
private EntityManagerInterface $entityManager,
private AccessLogRepository $accessLogRepository,
private CustomerRepository $customerRepository
) {
}
public function onAuthenticationSuccess(InteractiveLoginEvent $event): void
{
$ip = $_SERVER['REMOTE_ADDR'];
/** @var Customer */
$customer = $event->getAuthenticationToken()->getUser();
$this->logLogin($ip, $customer, 'success');
$this->setLastLoginOfCustomer($customer);
}
public function onAuthenticationFailure(LoginFailureEvent $event): void
{
$ip = $_SERVER['REMOTE_ADDR'];
$customer = $event?->getPassport()?->getUser();
$this->logLogin($ip, $customer);
$failures = $this->getFailedLoginAttemptsByIp($_SERVER['REMOTE_ADDR']);
$this->slowDownResponseWhenToManyFailures($failures);
}
public static function getSubscribedEvents(): array
{
return [
LoginFailureEvent::class => 'onAuthenticationFailure',
InteractiveLoginEvent::class => 'onAuthenticationSuccess',
];
}
private function setLastLoginOfCustomer(Customer $customer)
{
$customer->setLastLogin(new \DateTime(date('Y-m-d H:i:s')));
$customer->disableUpdateTimestamps();
$this->entityManager->persist($customer);
$this->entityManager->flush();
}
private function logLogin(string $ip, ?Customer $customer, string $type = 'failure'): void
{
$accessLog = new AccessLog();
$accessLog->setCustomer($customer);
$accessLog->setType($type);
$accessLog->setCreated(new \DateTime(date('Y-m-d H:i:s')));
$accessLog->setIp($ip);
$this->entityManager->persist($accessLog);
$this->entityManager->flush();
}
private function getFailedLoginAttemptsByIp(string $ip): int
{
return $this->accessLogRepository->findFailureQtyByIp($ip);
}
private function slowDownResponseWhenToManyFailures(int $failures): void
{
if ($failures > self::TO_MANY_FAILURES) {
sleep(10);
}
}
}