vendor/symfony/security-core/Authorization/AccessDecisionManager.php line 67

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 Symfony\Component\Security\Core\Authorization;
  11. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  12. use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface;
  13. use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeStrategy;
  14. use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface;
  15. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  16. use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
  17. /**
  18.  * AccessDecisionManager is the base class for all access decision managers
  19.  * that use decision voters.
  20.  *
  21.  * @author Fabien Potencier <fabien@symfony.com>
  22.  */
  23. final class AccessDecisionManager implements AccessDecisionManagerInterface
  24. {
  25.     private const VALID_VOTES = [
  26.         VoterInterface::ACCESS_GRANTED => true,
  27.         VoterInterface::ACCESS_DENIED => true,
  28.         VoterInterface::ACCESS_ABSTAIN => true,
  29.     ];
  30.     private iterable $voters;
  31.     private array $votersCacheAttributes = [];
  32.     private array $votersCacheObject = [];
  33.     private AccessDecisionStrategyInterface $strategy;
  34.     /**
  35.      * @param iterable<mixed, VoterInterface> $voters An array or an iterator of VoterInterface instances
  36.      */
  37.     public function __construct(iterable $voters = [], AccessDecisionStrategyInterface $strategy null)
  38.     {
  39.         $this->voters $voters;
  40.         $this->strategy $strategy ?? new AffirmativeStrategy();
  41.     }
  42.     /**
  43.      * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
  44.      */
  45.     public function decide(TokenInterface $token, array $attributesmixed $object nullbool $allowMultipleAttributes false): bool
  46.     {
  47.         // Special case for AccessListener, do not remove the right side of the condition before 6.0
  48.         if (\count($attributes) > && !$allowMultipleAttributes) {
  49.             throw new InvalidArgumentException(sprintf('Passing more than one Security attribute to "%s()" is not supported.'__METHOD__));
  50.         }
  51.         return $this->strategy->decide(
  52.             $this->collectResults($token$attributes$object)
  53.         );
  54.     }
  55.     /**
  56.      * @return \Traversable<int, int>
  57.      */
  58.     private function collectResults(TokenInterface $token, array $attributesmixed $object): \Traversable
  59.     {
  60.         foreach ($this->getVoters($attributes$object) as $voter) {
  61.             $result $voter->vote($token$object$attributes);
  62.             if (!\is_int($result) || !(self::VALID_VOTES[$result] ?? false)) {
  63.                 throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.'get_debug_type($voter), VoterInterface::class, var_export($resulttrue)));
  64.             }
  65.             yield $result;
  66.         }
  67.     }
  68.     /**
  69.      * @return iterable<mixed, VoterInterface>
  70.      */
  71.     private function getVoters(array $attributes$object null): iterable
  72.     {
  73.         $keyAttributes = [];
  74.         foreach ($attributes as $attribute) {
  75.             $keyAttributes[] = \is_string($attribute) ? $attribute null;
  76.         }
  77.         // use `get_class` to handle anonymous classes
  78.         $keyObject \is_object($object) ? $object::class : get_debug_type($object);
  79.         foreach ($this->voters as $key => $voter) {
  80.             if (!$voter instanceof CacheableVoterInterface) {
  81.                 yield $voter;
  82.                 continue;
  83.             }
  84.             $supports true;
  85.             // The voter supports the attributes if it supports at least one attribute of the list
  86.             foreach ($keyAttributes as $keyAttribute) {
  87.                 if (null === $keyAttribute) {
  88.                     $supports true;
  89.                 } elseif (!isset($this->votersCacheAttributes[$keyAttribute][$key])) {
  90.                     $this->votersCacheAttributes[$keyAttribute][$key] = $supports $voter->supportsAttribute($keyAttribute);
  91.                 } else {
  92.                     $supports $this->votersCacheAttributes[$keyAttribute][$key];
  93.                 }
  94.                 if ($supports) {
  95.                     break;
  96.                 }
  97.             }
  98.             if (!$supports) {
  99.                 continue;
  100.             }
  101.             if (!isset($this->votersCacheObject[$keyObject][$key])) {
  102.                 $this->votersCacheObject[$keyObject][$key] = $supports $voter->supportsType($keyObject);
  103.             } else {
  104.                 $supports $this->votersCacheObject[$keyObject][$key];
  105.             }
  106.             if (!$supports) {
  107.                 continue;
  108.             }
  109.             yield $voter;
  110.         }
  111.     }
  112. }