vendor/symfony/security-acl/Voter/AclVoter.php line 65

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\Acl\Voter;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
  13. use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
  14. use Symfony\Component\Security\Acl\Model\AclProviderInterface;
  15. use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
  16. use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
  17. use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
  18. use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
  19. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  20. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  21. if (class_exists(\Symfony\Component\Security\Core\Security::class)) {
  22. /**
  23. * @internal
  24. */
  25. trait AclVoterTrait
  26. {
  27. public function vote(TokenInterface $token, $subject, array $attributes)
  28. {
  29. return $this->doVote($token, $subject, $attributes);
  30. }
  31. }
  32. } else {
  33. /**
  34. * @internal
  35. */
  36. trait AclVoterTrait
  37. {
  38. public function vote(TokenInterface $token, mixed $subject, array $attributes): int
  39. {
  40. return $this->doVote($token, $subject, $attributes);
  41. }
  42. }
  43. }
  44. /**
  45. * This voter can be used as a base class for implementing your own permissions.
  46. *
  47. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  48. */
  49. class AclVoter implements VoterInterface
  50. {
  51. use AclVoterTrait;
  52. private $aclProvider;
  53. private $permissionMap;
  54. private $objectIdentityRetrievalStrategy;
  55. private $securityIdentityRetrievalStrategy;
  56. private $allowIfObjectIdentityUnavailable;
  57. private $logger;
  58. public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null, $allowIfObjectIdentityUnavailable = true)
  59. {
  60. $this->aclProvider = $aclProvider;
  61. $this->permissionMap = $permissionMap;
  62. $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy;
  63. $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy;
  64. $this->logger = $logger;
  65. $this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable;
  66. }
  67. public function supportsAttribute($attribute)
  68. {
  69. return \is_string($attribute) && $this->permissionMap->contains($attribute);
  70. }
  71. private function doVote(TokenInterface $token, $subject, array $attributes): int
  72. {
  73. foreach ($attributes as $attribute) {
  74. if (!$this->supportsAttribute($attribute)) {
  75. continue;
  76. }
  77. if (null === $masks = $this->permissionMap->getMasks($attribute, $subject)) {
  78. continue;
  79. }
  80. if (null === $subject) {
  81. if (null !== $this->logger) {
  82. $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain'));
  83. }
  84. return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
  85. } elseif ($subject instanceof FieldVote) {
  86. $field = $subject->getField();
  87. $subject = $subject->getDomainObject();
  88. } else {
  89. $field = null;
  90. }
  91. if ($subject instanceof ObjectIdentityInterface) {
  92. $oid = $subject;
  93. } elseif (null === $oid = $this->objectIdentityRetrievalStrategy->getObjectIdentity($subject)) {
  94. if (null !== $this->logger) {
  95. $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain'));
  96. }
  97. return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
  98. }
  99. if (!$this->supportsClass($oid->getType())) {
  100. return self::ACCESS_ABSTAIN;
  101. }
  102. $sids = $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token);
  103. try {
  104. $acl = $this->aclProvider->findAcl($oid, $sids);
  105. if (null === $field && $acl->isGranted($masks, $sids, false)) {
  106. if (null !== $this->logger) {
  107. $this->logger->debug('ACL found, permission granted. Voting to grant access.');
  108. }
  109. return self::ACCESS_GRANTED;
  110. } elseif (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) {
  111. if (null !== $this->logger) {
  112. $this->logger->debug('ACL found, permission granted. Voting to grant access.');
  113. }
  114. return self::ACCESS_GRANTED;
  115. }
  116. if (null !== $this->logger) {
  117. $this->logger->debug('ACL found, insufficient permissions. Voting to deny access.');
  118. }
  119. return self::ACCESS_DENIED;
  120. } catch (AclNotFoundException $e) {
  121. if (null !== $this->logger) {
  122. $this->logger->debug('No ACL found for the object identity. Voting to deny access.');
  123. }
  124. return self::ACCESS_DENIED;
  125. } catch (NoAceFoundException $e) {
  126. if (null !== $this->logger) {
  127. $this->logger->debug('ACL found, no ACE applicable. Voting to deny access.');
  128. }
  129. return self::ACCESS_DENIED;
  130. }
  131. }
  132. // no attribute was supported
  133. return self::ACCESS_ABSTAIN;
  134. }
  135. /**
  136. * You can override this method when writing a voter for a specific domain
  137. * class.
  138. *
  139. * @param string $class The class name
  140. *
  141. * @return bool
  142. */
  143. public function supportsClass($class)
  144. {
  145. return true;
  146. }
  147. }