<?php
/*
* This file is part of the nellapp-core package.
*
* (c) Benjamin Georgeault
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\EventSubscriber;
use App\Entity\Account\SsoAppSession;
use App\Entity\Account\User;
use App\Repository\Account\SsoAppSessionRepository;
use Nellapp\Bundle\SDKBundle\Routing\UrlGeneratorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Event\LogoutEvent;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* Class LogoutSubscriber
*
* @author Benjamin Georgeault
*/
class LogoutSubscriber implements EventSubscriberInterface
{
public function __construct(
private SsoAppSessionRepository $ssoAppSessionRepository,
private HttpClientInterface $httpClient,
private UrlGeneratorInterface $internalUrlGenerator,
private LoggerInterface $logger,
)
{
}
public static function getSubscribedEvents(): array
{
return [
LogoutEvent::class => 'logout',
];
}
public function logout(LogoutEvent $event): void
{
if (null === $user = $this->getUser($event->getToken())) {
return;
}
$request = $event->getRequest();
$this->triggerGlobalLogout($user, $request);
if (!$request->query->has('target')) {
return;
}
if (!filter_var($target = $request->query->get('target'), FILTER_VALIDATE_URL)) {
return;
}
$event->setResponse(new RedirectResponse($target));
}
private function getUser(TokenInterface $token): ?User
{
if (($user = $token->getUser()) instanceof User) {
return $user;
}
return null;
}
private function triggerGlobalLogout(User $user, Request $request): void
{
$deviceId = $request->cookies->get('DEVICE_ID');
if (!$deviceId) {
return;
}
$sessions = $this->ssoAppSessionRepository->findActiveByUserAndDevice($user, $deviceId);
/** Should only have one active session by app and device */
/** @var SsoAppSession $session */
foreach ($sessions as $session) {
$app = $session->getApp();
$payload = [
'sessionIds' => [$session->getSessionId()],
];
try {
if ($this->internalUrlGenerator->exist($app->getQueueName(), 'api_account_global_logout')) {
$this->httpClient->request(
'POST',
$this->internalUrlGenerator->generate($app->getQueueName(), 'api_account_global_logout'),
[
'json' => $payload,
'headers' => [
'Authorization' => 'Core ' . $app->getSecret(),
]
]
);
}
} catch (TransportExceptionInterface $e) {
$this->logger->error($e);
}
$this->ssoAppSessionRepository->revokeSession($session);
}
}
}