src/EventSubscriber/AdminAuthenticationSubscriber.php line 55

Open in your IDE?
  1. <?php
  2. namespace App\EventSubscriber;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  4. use Symfony\Component\HttpFoundation\JsonResponse;
  5. use Symfony\Component\HttpFoundation\RedirectResponse;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpKernel\Event\RequestEvent;
  8. use Symfony\Component\HttpKernel\KernelEvents;
  9. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  10. /**
  11.  * SÉCURITÉ (correctif d'urgence) : verrou central d'authentification.
  12.  *
  13.  * Avant, chaque page d'administration devait elle-même vérifier la session ; plus de 200 routes
  14.  * (ajout/suppression d'administrateurs, envois de fichiers, clés Stripe, etc.) ne le faisaient
  15.  * pas et étaient donc accessibles à n'importe qui sans connexion.
  16.  *
  17.  * Désormais, toute requête vers le backoffice est refusée tant que la session ne contient pas
  18.  * la marque de connexion posée par le formulaire de connexion, sauf pour les quelques routes
  19.  * strictement nécessaires pour se connecter (liste ci-dessous).
  20.  */
  21. class AdminAuthenticationSubscriber implements EventSubscriberInterface
  22. {
  23.     /** Clé de session posée à la connexion réussie (voir GlobalAdmin::verifCnx). */
  24.     public const SESSION_KEY = 'supAdT9m2XJzn4';
  25.     /** Routes accessibles SANS être connecté. */
  26.     private const PUBLIC_ROUTES = [
  27.         'connexion',
  28.         'deconnexion',
  29.         'login_user',
  30.         'verif_cnx',
  31.         'verif_cnx1',
  32.         'confirm_connexion',
  33.         'confirm_access',
  34.         'confirm_access_old',
  35.         'fos_js_routing_js',
  36.     ];
  37.     private UrlGeneratorInterface $urlGenerator;
  38.     public function __construct(UrlGeneratorInterface $urlGenerator)
  39.     {
  40.         $this->urlGenerator = $urlGenerator;
  41.     }
  42.     public static function getSubscribedEvents(): array
  43.     {
  44.         // Priorité 7 : après le routeur (32) qui détermine _route, avant les contrôleurs.
  45.         return [KernelEvents::REQUEST => ['onKernelRequest', 7]];
  46.     }
  47.     public function onKernelRequest(RequestEvent $event): void
  48.     {
  49.         if (!$event->isMainRequest()) {
  50.             return;
  51.         }
  52.         $request = $event->getRequest();
  53.         $route = (string) $request->attributes->get('_route', '');
  54.         // Pas de route trouvée (404 géré par Symfony) ou routes techniques (_wdt, _profiler, _error...)
  55.         if ($route === '' || $route[0] === '_') {
  56.             return;
  57.         }
  58.         if (\in_array($route, self::PUBLIC_ROUTES, true)) {
  59.             return;
  60.         }
  61.         if ($this->isAuthenticated($request)) {
  62.             return;
  63.         }
  64.         if ($request->isXmlHttpRequest() || $this->wantsJson($request)) {
  65.             $event->setResponse(new JsonResponse(['error' => 'Authentification requise'], 401));
  66.             return;
  67.         }
  68.         $event->setResponse(new RedirectResponse($this->urlGenerator->generate('connexion')));
  69.     }
  70.     private function isAuthenticated(Request $request): bool
  71.     {
  72.         if (!$request->hasSession()) {
  73.             return false;
  74.         }
  75.         $session = $request->getSession();
  76.         // Ne pas démarrer de session inutilement pour un visiteur sans cookie.
  77.         if (!$request->hasPreviousSession() && !$session->isStarted()) {
  78.             return false;
  79.         }
  80.         $admin = $session->get(self::SESSION_KEY);
  81.         // La déconnexion remplace la valeur par '' : une valeur vide = non connecté.
  82.         return \is_array($admin) && !empty($admin['email']) && !empty($admin['id']);
  83.     }
  84.     private function wantsJson(Request $request): bool
  85.     {
  86.         return str_contains((string) $request->headers->get('Accept', ''), 'application/json');
  87.     }
  88. }