<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* SÉCURITÉ (correctif d'urgence) : verrou central d'authentification.
*
* Avant, chaque page d'administration devait elle-même vérifier la session ; plus de 200 routes
* (ajout/suppression d'administrateurs, envois de fichiers, clés Stripe, etc.) ne le faisaient
* pas et étaient donc accessibles à n'importe qui sans connexion.
*
* Désormais, toute requête vers le backoffice est refusée tant que la session ne contient pas
* la marque de connexion posée par le formulaire de connexion, sauf pour les quelques routes
* strictement nécessaires pour se connecter (liste ci-dessous).
*/
class AdminAuthenticationSubscriber implements EventSubscriberInterface
{
/** Clé de session posée à la connexion réussie (voir GlobalAdmin::verifCnx). */
public const SESSION_KEY = 'supAdT9m2XJzn4';
/** Routes accessibles SANS être connecté. */
private const PUBLIC_ROUTES = [
'connexion',
'deconnexion',
'login_user',
'verif_cnx',
'verif_cnx1',
'confirm_connexion',
'confirm_access',
'confirm_access_old',
'fos_js_routing_js',
];
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public static function getSubscribedEvents(): array
{
// Priorité 7 : après le routeur (32) qui détermine _route, avant les contrôleurs.
return [KernelEvents::REQUEST => ['onKernelRequest', 7]];
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$route = (string) $request->attributes->get('_route', '');
// Pas de route trouvée (404 géré par Symfony) ou routes techniques (_wdt, _profiler, _error...)
if ($route === '' || $route[0] === '_') {
return;
}
if (\in_array($route, self::PUBLIC_ROUTES, true)) {
return;
}
if ($this->isAuthenticated($request)) {
return;
}
if ($request->isXmlHttpRequest() || $this->wantsJson($request)) {
$event->setResponse(new JsonResponse(['error' => 'Authentification requise'], 401));
return;
}
$event->setResponse(new RedirectResponse($this->urlGenerator->generate('connexion')));
}
private function isAuthenticated(Request $request): bool
{
if (!$request->hasSession()) {
return false;
}
$session = $request->getSession();
// Ne pas démarrer de session inutilement pour un visiteur sans cookie.
if (!$request->hasPreviousSession() && !$session->isStarted()) {
return false;
}
$admin = $session->get(self::SESSION_KEY);
// La déconnexion remplace la valeur par '' : une valeur vide = non connecté.
return \is_array($admin) && !empty($admin['email']) && !empty($admin['id']);
}
private function wantsJson(Request $request): bool
{
return str_contains((string) $request->headers->get('Accept', ''), 'application/json');
}
}