vendor/sensio/framework-extra-bundle/src/EventListener/SecurityListener.php line 54

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
  11. use Psr\Log\LoggerInterface;
  12. use Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter;
  13. use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Component\HttpKernel\Event\KernelEvent;
  16. use Symfony\Component\HttpKernel\Exception\HttpException;
  17. use Symfony\Component\HttpKernel\KernelEvents;
  18. use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
  19. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  20. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  21. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  22. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  23. use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
  24. /**
  25.  * SecurityListener handles security restrictions on controllers.
  26.  *
  27.  * @author Fabien Potencier <fabien@symfony.com>
  28.  */
  29. class SecurityListener implements EventSubscriberInterface
  30. {
  31.     private $argumentNameConverter;
  32.     private $tokenStorage;
  33.     private $authChecker;
  34.     private $language;
  35.     private $trustResolver;
  36.     private $roleHierarchy;
  37.     private $logger;
  38.     public function __construct(ArgumentNameConverter $argumentNameConverterExpressionLanguage $language nullAuthenticationTrustResolverInterface $trustResolver nullRoleHierarchyInterface $roleHierarchy nullTokenStorageInterface $tokenStorage nullAuthorizationCheckerInterface $authChecker nullLoggerInterface $logger null)
  39.     {
  40.         $this->argumentNameConverter $argumentNameConverter;
  41.         $this->tokenStorage $tokenStorage;
  42.         $this->authChecker $authChecker;
  43.         $this->language $language;
  44.         $this->trustResolver $trustResolver;
  45.         $this->roleHierarchy $roleHierarchy;
  46.         $this->logger $logger;
  47.     }
  48.     public function onKernelControllerArguments(KernelEvent $event)
  49.     {
  50.         $request $event->getRequest();
  51.         if (!$configurations $request->attributes->get('_security')) {
  52.             return;
  53.         }
  54.         if (null === $this->tokenStorage || null === $this->trustResolver) {
  55.             throw new \LogicException('To use the @Security tag, you need to install the Symfony Security bundle.');
  56.         }
  57.         if (null === $this->tokenStorage->getToken()) {
  58.             throw new AccessDeniedException('No user token or you forgot to put your controller behind a firewall while using a @Security tag.');
  59.         }
  60.         if (null === $this->language) {
  61.             throw new \LogicException('To use the @Security tag, you need to use the Security component 2.4 or newer and install the ExpressionLanguage component.');
  62.         }
  63.         foreach ($configurations as $configuration) {
  64.             if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($event))) {
  65.                 if ($statusCode $configuration->getStatusCode()) {
  66.                     throw new HttpException($statusCode$configuration->getMessage());
  67.                 }
  68.                 throw new AccessDeniedException($configuration->getMessage() ?: sprintf('Expression "%s" denied access.'$configuration->getExpression()));
  69.             }
  70.         }
  71.     }
  72.     // code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
  73.     private function getVariables(KernelEvent $event)
  74.     {
  75.         $request $event->getRequest();
  76.         $token $this->tokenStorage->getToken();
  77.         $variables = [
  78.             'token' => $token,
  79.             'user' => $token->getUser(),
  80.             'object' => $request,
  81.             'subject' => $request,
  82.             'request' => $request,
  83.             'roles' => $this->getRoles($token),
  84.             'trust_resolver' => $this->trustResolver,
  85.             // needed for the is_granted expression function
  86.             'auth_checker' => $this->authChecker,
  87.         ];
  88.         $controllerArguments $this->argumentNameConverter->getControllerArguments($event);
  89.         if ($diff array_intersect(array_keys($variables), array_keys($controllerArguments))) {
  90.             foreach ($diff as $key => $variableName) {
  91.                 if ($variables[$variableName] === $controllerArguments[$variableName]) {
  92.                     unset($diff[$key]);
  93.                 }
  94.             }
  95.             if ($diff) {
  96.                 $singular === \count($diff);
  97.                 if (null !== $this->logger) {
  98.                     $this->logger->warning(sprintf('Controller argument%s "%s" collided with the built-in security expression variables. The built-in value%s are being used for the @Security expression.'$singular '' 's'implode('", "'$diff), $singular 's' ''));
  99.                 }
  100.             }
  101.         }
  102.         // controller variables should also be accessible
  103.         return array_merge($controllerArguments$variables);
  104.     }
  105.     private function getRoles(TokenInterface $token): array
  106.     {
  107.         if (method_exists($this->roleHierarchy'getReachableRoleNames')) {
  108.             if (null !== $this->roleHierarchy) {
  109.                 $roles $this->roleHierarchy->getReachableRoleNames($token->getRoleNames());
  110.             } else {
  111.                 $roles $token->getRoleNames();
  112.             }
  113.         } else {
  114.             if (null !== $this->roleHierarchy) {
  115.                 $roles $this->roleHierarchy->getReachableRoles($token->getRoles());
  116.             } else {
  117.                 $roles $token->getRoles();
  118.             }
  119.             $roles array_map(function ($role) {
  120.                 return $role->getRole();
  121.             }, $roles);
  122.         }
  123.         return $roles;
  124.     }
  125.     /**
  126.      * @return array
  127.      */
  128.     public static function getSubscribedEvents()
  129.     {
  130.         return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
  131.     }
  132. }