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

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\Authorization\AuthorizationCheckerInterface;
  21. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  22. use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
  23. /**
  24.  * SecurityListener handles security restrictions on controllers.
  25.  *
  26.  * @author Fabien Potencier <fabien@symfony.com>
  27.  */
  28. class SecurityListener implements EventSubscriberInterface
  29. {
  30.     private $argumentNameConverter;
  31.     private $tokenStorage;
  32.     private $authChecker;
  33.     private $language;
  34.     private $trustResolver;
  35.     private $roleHierarchy;
  36.     private $logger;
  37.     public function __construct(ArgumentNameConverter $argumentNameConverterExpressionLanguage $language nullAuthenticationTrustResolverInterface $trustResolver nullRoleHierarchyInterface $roleHierarchy nullTokenStorageInterface $tokenStorage nullAuthorizationCheckerInterface $authChecker nullLoggerInterface $logger null)
  38.     {
  39.         $this->argumentNameConverter $argumentNameConverter;
  40.         $this->tokenStorage $tokenStorage;
  41.         $this->authChecker $authChecker;
  42.         $this->language $language;
  43.         $this->trustResolver $trustResolver;
  44.         $this->roleHierarchy $roleHierarchy;
  45.         $this->logger $logger;
  46.     }
  47.     public function onKernelControllerArguments(KernelEvent $event)
  48.     {
  49.         $request $event->getRequest();
  50.         if (!$configurations $request->attributes->get('_security')) {
  51.             return;
  52.         }
  53.         if (null === $this->tokenStorage || null === $this->trustResolver) {
  54.             throw new \LogicException('To use the @Security tag, you need to install the Symfony Security bundle.');
  55.         }
  56.         if (null === $this->tokenStorage->getToken()) {
  57.             throw new \LogicException('To use the @Security tag, your controller needs to be behind a firewall.');
  58.         }
  59.         if (null === $this->language) {
  60.             throw new \LogicException('To use the @Security tag, you need to use the Security component 2.4 or newer and install the ExpressionLanguage component.');
  61.         }
  62.         foreach ($configurations as $configuration) {
  63.             if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($event))) {
  64.                 if ($statusCode $configuration->getStatusCode()) {
  65.                     throw new HttpException($statusCode$configuration->getMessage());
  66.                 }
  67.                 throw new AccessDeniedException($configuration->getMessage() ?: sprintf('Expression "%s" denied access.'$configuration->getExpression()));
  68.             }
  69.         }
  70.     }
  71.     // code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
  72.     private function getVariables(KernelEvent $event)
  73.     {
  74.         $request $event->getRequest();
  75.         $token $this->tokenStorage->getToken();
  76.         if (method_exists($this->roleHierarchy'getReachableRoleNames')) {
  77.             if (null !== $this->roleHierarchy) {
  78.                 $roles $this->roleHierarchy->getReachableRoleNames($token->getRoleNames());
  79.             } else {
  80.                 $roles $token->getRoleNames();
  81.             }
  82.         } else {
  83.             if (null !== $this->roleHierarchy) {
  84.                 $roles $this->roleHierarchy->getReachableRoles($token->getRoles());
  85.             } else {
  86.                 $roles $token->getRoles();
  87.             }
  88.             $roles array_map(function ($role) {
  89.                 return $role->getRole();
  90.             }, $roles);
  91.         }
  92.         $variables = [
  93.             'token' => $token,
  94.             'user' => $token->getUser(),
  95.             'object' => $request,
  96.             'subject' => $request,
  97.             'request' => $request,
  98.             'roles' => $roles,
  99.             'trust_resolver' => $this->trustResolver,
  100.             // needed for the is_granted expression function
  101.             'auth_checker' => $this->authChecker,
  102.         ];
  103.         $controllerArguments $this->argumentNameConverter->getControllerArguments($event);
  104.         if ($diff array_intersect(array_keys($variables), array_keys($controllerArguments))) {
  105.             foreach ($diff as $key => $variableName) {
  106.                 if ($variables[$variableName] === $controllerArguments[$variableName]) {
  107.                     unset($diff[$key]);
  108.                 }
  109.             }
  110.             if ($diff) {
  111.                 $singular === \count($diff);
  112.                 if (null !== $this->logger) {
  113.                     $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' ''));
  114.                 }
  115.             }
  116.         }
  117.         // controller variables should also be accessible
  118.         return array_merge($controllerArguments$variables);
  119.     }
  120.     /**
  121.      * {@inheritdoc}
  122.      */
  123.     public static function getSubscribedEvents()
  124.     {
  125.         return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
  126.     }
  127. }