src/Security/TenantOwnershipSubscriber.php line 36

Open in your IDE?
  1. <?php
  2. namespace App\Security;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  4. use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
  5. use Symfony\Component\HttpKernel\KernelEvents;
  6. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  7. use Symfony\Component\Security\Core\Security;
  8. /**
  9.  * Cloisonnement multi-entreprise (SaaS) : toute entité métier injectée dans un
  10.  * contrôleur (ParamConverter "{id}") doit appartenir à l'entreprise connectée.
  11.  *
  12.  * Point unique de contrôle : les contrôleurs n'ont pas à vérifier eux-mêmes le
  13.  * propriétaire des entités reçues en argument. Une entité d'une autre
  14.  * entreprise répond 404 (on ne révèle pas son existence), un visiteur non
  15.  * connecté est renvoyé vers la connexion. Les règles sont dans TenantGuard.
  16.  */
  17. class TenantOwnershipSubscriber implements EventSubscriberInterface
  18. {
  19.     private $security;
  20.     private $tenantGuard;
  21.     public function __construct(Security $security, TenantGuard $tenantGuard)
  22.     {
  23.         $this->security = $security;
  24.         $this->tenantGuard = $tenantGuard;
  25.     }
  26.     public static function getSubscribedEvents(): array
  27.     {
  28.         return [KernelEvents::CONTROLLER_ARGUMENTS => 'onControllerArguments'];
  29.     }
  30.     public function onControllerArguments(ControllerArgumentsEvent $event): void
  31.     {
  32.         foreach ($event->getArguments() as $argument) {
  33.             if (!is_object($argument) || !$this->tenantGuard->isTenantEntity($argument)) {
  34.                 continue;
  35.             }
  36.             if (!$this->security->getUser()) {
  37.                 throw new AccessDeniedException('Connexion requise.');
  38.             }
  39.             $this->tenantGuard->denyUnlessCanAccess($argument);
  40.         }
  41.     }
  42. }