vendor/symfony/security-http/EventListener/IsGrantedAttributeListener.php line 69

  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 Symfony\Component\Security\Http\EventListener;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. use Symfony\Component\ExpressionLanguage\Expression;
  13. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
  16. use Symfony\Component\HttpKernel\Exception\HttpException;
  17. use Symfony\Component\HttpKernel\KernelEvents;
  18. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  19. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  20. use Symfony\Component\Security\Core\Exception\RuntimeException;
  21. use Symfony\Component\Security\Http\Attribute\IsGranted;
  22. /**
  23.  * Handles the IsGranted attribute on controllers.
  24.  *
  25.  * @author Ryan Weaver <ryan@knpuniversity.com>
  26.  */
  27. class IsGrantedAttributeListener implements EventSubscriberInterface
  28. {
  29.     public function __construct(
  30.         private readonly AuthorizationCheckerInterface $authChecker,
  31.         private ?ExpressionLanguage $expressionLanguage null,
  32.     ) {
  33.     }
  34.     public function onKernelControllerArguments(ControllerArgumentsEvent $event)
  35.     {
  36.         /** @var IsGranted[] $attributes */
  37.         if (!\is_array($attributes $event->getAttributes()[IsGranted::class] ?? null)) {
  38.             return;
  39.         }
  40.         $request $event->getRequest();
  41.         $arguments $event->getNamedArguments();
  42.         foreach ($attributes as $attribute) {
  43.             $subject null;
  44.             if ($subjectRef $attribute->subject) {
  45.                 if (\is_array($subjectRef)) {
  46.                     foreach ($subjectRef as $refKey => $ref) {
  47.                         $subject[\is_string($refKey) ? $refKey : (string) $ref] = $this->getIsGrantedSubject($ref$request$arguments);
  48.                     }
  49.                 } else {
  50.                     $subject $this->getIsGrantedSubject($subjectRef$request$arguments);
  51.                 }
  52.             }
  53.             if (!$this->authChecker->isGranted($attribute->attribute$subject)) {
  54.                 $message $attribute->message ?: sprintf('Access Denied by #[IsGranted(%s)] on controller'$this->getIsGrantedString($attribute));
  55.                 if ($statusCode $attribute->statusCode) {
  56.                     throw new HttpException($statusCode$message);
  57.                 }
  58.                 $accessDeniedException = new AccessDeniedException($message);
  59.                 $accessDeniedException->setAttributes($attribute->attribute);
  60.                 $accessDeniedException->setSubject($subject);
  61.                 throw $accessDeniedException;
  62.             }
  63.         }
  64.     }
  65.     public static function getSubscribedEvents(): array
  66.     {
  67.         return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments'20]];
  68.     }
  69.     private function getIsGrantedSubject(string|Expression $subjectRefRequest $request, array $arguments): mixed
  70.     {
  71.         if ($subjectRef instanceof Expression) {
  72.             $this->expressionLanguage ??= new ExpressionLanguage();
  73.             return $this->expressionLanguage->evaluate($subjectRef, [
  74.                 'request' => $request,
  75.                 'args' => $arguments,
  76.             ]);
  77.         }
  78.         if (!\array_key_exists($subjectRef$arguments)) {
  79.             throw new RuntimeException(sprintf('Could not find the subject "%s" for the #[IsGranted] attribute. Try adding a "$%s" argument to your controller method.'$subjectRef$subjectRef));
  80.         }
  81.         return $arguments[$subjectRef];
  82.     }
  83.     private function getIsGrantedString(IsGranted $isGranted): string
  84.     {
  85.         $processValue = fn ($value) => sprintf($value instanceof Expression 'new Expression("%s")' '"%s"'$value);
  86.         $argsString $processValue($isGranted->attribute);
  87.         if (null !== $subject $isGranted->subject) {
  88.             $subject = !\is_array($subject) ? $processValue($subject) : array_map(function ($key$value) use ($processValue) {
  89.                 $value $processValue($value);
  90.                 return \is_string($key) ? sprintf('"%s" => %s'$key$value) : $value;
  91.             }, array_keys($subject), $subject);
  92.             $argsString .= ', '.(!\is_array($subject) ? $subject '['.implode(', '$subject).']');
  93.         }
  94.         return $argsString;
  95.     }
  96. }