<?php
namespace App\Listener;
use App\Entity\User;
use App\Services\DTV\YamlConfig\YamlReader;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Doctrine\Persistence\ManagerRegistry;
class CheckCguListener
{
private TokenStorageInterface $token;
private RouterInterface $router;
private EntityManagerInterface $em;
private YamlReader $yamlReader;
private RequestStack $requestStack;
private LoggerInterface $logger;
private ManagerRegistry $managerRegistry;
public function __construct(
EntityManagerInterface $em,
TokenStorageInterface $token,
RouterInterface $router,
YamlReader $yamlReader,
RequestStack $requestStack,
LoggerInterface $logger,
ManagerRegistry $managerRegistry
) {
$this->token = $token;
$this->router = $router;
$this->em = $em;
$this->yamlReader = $yamlReader;
$this->requestStack = $requestStack;
$this->logger = $logger;
$this->managerRegistry = $managerRegistry;
}
/**
* @param ResponseEvent $event
*
* @return void
*/
public function onKernelResponse(ResponseEvent $event)
{
$request = $event->getRequest();
$global = $this->yamlReader->getGlobal();
// Dans Symfony, le type de requête "1" signifie généralement une requête principale
if ($event->getRequestType() !== 1) {
return;
}
if ($request->isXmlHttpRequest()) {
return;
}
if ($request->isXmlHttpRequest()) {
return;
}
// si on est sur une page statique, on ne check pas les CGU
if (in_array(
$request->get('_route'),
[
'static_file_folder',
'static_project_file',
'front_common_css_custom',
'front_user_accept_cgu_first',
'front_user_accept_cgu_only',
'front_user_welcome_register',
'front_user_password',
],
)) {
return;
}
if (NULL !== $this->token->getToken()) {
$token = $this->token->getToken();
// L'utilisateur est en train d'impersonner un autre utilisateur, donc on ne fait rien
if ($token && in_array('ROLE_PREVIOUS_ADMIN', $token->getRoleNames())) {
return;
}
/** @var User $user */
$user = $this->token->getToken()->getUser();
$user = $this->em->getRepository(User::class)->findOneBy([
'id' => $user->getId()
]);
if ($user instanceof User) {
$this->updateUserLastActivity($user);
$cguToValidate = true;
if(isset($global['cgu_to_validate']) && $global['cgu_to_validate'] === FALSE) {
$cguToValidate = false;
}
if($cguToValidate){
// L'utilisateur est de type developer ou super-admin, on passe directement en actif
if ($user->isDeveloper() || $user->isSuperAdmin()) {
if ($user->getStatus() == 'cgu_pending' || $user->getCguAt() === NULL) {
$user->setStatus('enabled')
->setCguAt(new DateTime())
;
$this->em->flush();
}
return;
} // Sinon, on passe par la validation des CGU
elseif (NULL === $user->getCguAt() || $user->getStatus() === 'cgu_pending') {
$event->setResponse(
new RedirectResponse(
$this->router->generate('front_user_accept_cgu_only'),
),
);
}
}
// L'utilisateur est en train d'impersonner un autre utilisateur, donc on ne fait rien
if ($token && in_array('ROLE_PREVIOUS_ADMIN', $token->getRoleNames())) {
return;
}
// TODO: Si on est sur un enfant d'un portail il faut gérer le fait de rediriger sur le portail avec un message clair
// Si le password de l'utilisateur est expiré, on le redirige vers la page de changement de mot de passe, sauf si on est déjà sur cette page
if (!$user->isDeveloper() && $request->get('_route') !== 'front_user_password')
{
$now = new DateTime();
$passwordUpdatedAt = $user->getPasswordUpdatedAt() ?? (new DateTime())->modify('-370 days');
$date = $now->diff($passwordUpdatedAt);
$global = $this->yamlReader->getGlobal();
$remainingPasswordValidityDays = $global[ 'login_security' ][ 'password_validation_days' ] ?? 365;
$rest = $remainingPasswordValidityDays - $date->days;
if ($rest < 0) {
$session = $this->requestStack->getSession();
$session->getFlashBag()->add(
'danger',
'Votre mot de passe a expiré ! Veuillez le mettre à jour.',
);
$event->setResponse(
new RedirectResponse(
$this->router->generate('saml_logout'),
),
);
}
}
}
}
}
/**
* Met à jour la date de dernière activité de l'utilisateur
*
* @param User $user Utilisateur
*
* @return void
*/
private function updateUserLastActivity(User $user): void
{
if(!$this->em->isOpen()){
$this->managerRegistry->resetManager();
$user = $this->em->getRepository(User::class)->findOneBy([
'id' => $user->getId()
]);
}else{
$this->em->refresh($user);
}
// Si la dernière activité de l'utilisateur date de moins de 1H, on ne fait rien
$now = new DateTime();
$timestampDebut = $user->getLastActivity() ? $user->getLastActivity()->getTimestamp() : 0;
$timestampFin = $now->getTimestamp();
$diff = $timestampFin - $timestampDebut;
if ($diff >= 3600) {
try {
$user->setLastActivity(new DateTime());
$this->em->flush();
} catch (\Exception $e) {
$this->logger->error('Impossible de mettre à jour la date de dernière activité de l\'utilisateur.', [
'user' => $user->getId(),
'error' => $e->getMessage(),
]);
}
}
}
}