vendor/symfony/src/Symfony/Component/Security/Acl/Domain/PermissionGrantingStrategy.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\Security\Acl\Domain;
       
    13 
       
    14 use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
       
    15 use Symfony\Component\Security\Acl\Exception\SidNotLoadedException;
       
    16 use Symfony\Component\Security\Acl\Model\AclInterface;
       
    17 use Symfony\Component\Security\Acl\Model\AuditLoggerInterface;
       
    18 use Symfony\Component\Security\Acl\Model\EntryInterface;
       
    19 use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
       
    20 use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
       
    21 
       
    22 /**
       
    23  * The permission granting strategy to apply to the access control list.
       
    24  *
       
    25  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
       
    26  */
       
    27 class PermissionGrantingStrategy implements PermissionGrantingStrategyInterface
       
    28 {
       
    29     const EQUAL = 'equal';
       
    30     const ALL   = 'all';
       
    31     const ANY   = 'any';
       
    32 
       
    33     private $auditLogger;
       
    34 
       
    35     /**
       
    36      * Sets the audit logger
       
    37      *
       
    38      * @param AuditLoggerInterface $auditLogger
       
    39      * @return void
       
    40      */
       
    41     public function setAuditLogger(AuditLoggerInterface $auditLogger)
       
    42     {
       
    43         $this->auditLogger = $auditLogger;
       
    44     }
       
    45 
       
    46     /**
       
    47      * {@inheritDoc}
       
    48      */
       
    49     public function isGranted(AclInterface $acl, array $masks, array $sids, $administrativeMode = false)
       
    50     {
       
    51         try {
       
    52             try {
       
    53                 $aces = $acl->getObjectAces();
       
    54 
       
    55                 if (!$aces) {
       
    56                     throw new NoAceFoundException();
       
    57                 }
       
    58 
       
    59                 return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
       
    60             } catch (NoAceFoundException $noObjectAce) {
       
    61                 $aces = $acl->getClassAces();
       
    62 
       
    63                 if (!$aces) {
       
    64                     throw $noObjectAce;
       
    65                 }
       
    66 
       
    67                 return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
       
    68             }
       
    69         } catch (NoAceFoundException $noClassAce) {
       
    70             if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) {
       
    71                 return $parentAcl->isGranted($masks, $sids, $administrativeMode);
       
    72             }
       
    73 
       
    74             throw $noClassAce;
       
    75         }
       
    76     }
       
    77 
       
    78     /**
       
    79      * {@inheritDoc}
       
    80      */
       
    81     public function isFieldGranted(AclInterface $acl, $field, array $masks, array $sids, $administrativeMode = false)
       
    82     {
       
    83         try {
       
    84             try {
       
    85                 $aces = $acl->getObjectFieldAces($field);
       
    86                 if (!$aces) {
       
    87                     throw new NoAceFoundException();
       
    88                 }
       
    89 
       
    90                 return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
       
    91             } catch (NoAceFoundException $noObjectAces) {
       
    92                 $aces = $acl->getClassFieldAces($field);
       
    93                 if (!$aces) {
       
    94                     throw $noObjectAces;
       
    95                 }
       
    96 
       
    97                 return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
       
    98             }
       
    99         } catch (NoAceFoundException $noClassAces) {
       
   100             if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) {
       
   101                 return $parentAcl->isFieldGranted($field, $masks, $sids, $administrativeMode);
       
   102             }
       
   103 
       
   104             throw $noClassAces;
       
   105         }
       
   106     }
       
   107 
       
   108     /**
       
   109      * Makes an authorization decision.
       
   110      *
       
   111      * The order of ACEs, and SIDs is significant; the order of permission masks
       
   112      * not so much. It is important to note that the more specific security
       
   113      * identities should be at the beginning of the SIDs array in order for this
       
   114      * strategy to produce intuitive authorization decisions.
       
   115      *
       
   116      * First, we will iterate over permissions, then over security identities.
       
   117      * For each combination of permission, and identity we will test the
       
   118      * available ACEs until we find one which is applicable.
       
   119      *
       
   120      * The first applicable ACE will make the ultimate decision for the
       
   121      * permission/identity combination. If it is granting, this method will return
       
   122      * true, if it is denying, the method will continue to check the next
       
   123      * permission/identity combination.
       
   124      *
       
   125      * This process is repeated until either a granting ACE is found, or no
       
   126      * permission/identity combinations are left. In the latter case, we will
       
   127      * call this method on the parent ACL if it exists, and isEntriesInheriting
       
   128      * is true. Otherwise, we will either throw an NoAceFoundException, or deny
       
   129      * access finally.
       
   130      *
       
   131      * @param AclInterface $acl
       
   132      * @param array        $aces               An array of ACE to check against
       
   133      * @param array        $masks              An array of permission masks
       
   134      * @param array        $sids               An array of SecurityIdentityInterface implementations
       
   135      * @param Boolean      $administrativeMode True turns off audit logging
       
   136      * @return Boolean true, or false; either granting, or denying access respectively.
       
   137      */
       
   138     private function hasSufficientPermissions(AclInterface $acl, array $aces, array $masks, array $sids, $administrativeMode)
       
   139     {
       
   140         $firstRejectedAce  = null;
       
   141 
       
   142         foreach ($masks as $requiredMask) {
       
   143             foreach ($sids as $sid) {
       
   144                 foreach ($aces as $ace) {
       
   145                     if ($sid->equals($ace->getSecurityIdentity()) && $this->isAceApplicable($requiredMask, $ace)) {
       
   146                         if ($ace->isGranting()) {
       
   147                             if (!$administrativeMode && null !== $this->auditLogger) {
       
   148                                 $this->auditLogger->logIfNeeded(true, $ace);
       
   149                             }
       
   150 
       
   151                             return true;
       
   152                         }
       
   153 
       
   154                         if (null === $firstRejectedAce) {
       
   155                             $firstRejectedAce = $ace;
       
   156                         }
       
   157 
       
   158                         break 2;
       
   159                     }
       
   160                 }
       
   161             }
       
   162         }
       
   163 
       
   164         if (null !== $firstRejectedAce) {
       
   165             if (!$administrativeMode && null !== $this->auditLogger) {
       
   166                 $this->auditLogger->logIfNeeded(false, $firstRejectedAce);
       
   167             }
       
   168 
       
   169             return false;
       
   170         }
       
   171 
       
   172         throw new NoAceFoundException();
       
   173     }
       
   174 
       
   175     /**
       
   176      * Determines whether the ACE is applicable to the given permission/security
       
   177      * identity combination.
       
   178      *
       
   179      * Per default, we support three different comparison strategies.
       
   180      *
       
   181      * Strategy ALL:
       
   182      * The ACE will be considered applicable when all the turned-on bits in the
       
   183      * required mask are also turned-on in the ACE mask.
       
   184      *
       
   185      * Strategy ANY:
       
   186      * The ACE will be considered applicable when any of the turned-on bits in
       
   187      * the required mask is also turned-on the in the ACE mask.
       
   188      *
       
   189      * Strategy EQUAL:
       
   190      * The ACE will be considered applicable when the bitmasks are equal.
       
   191      *
       
   192      * @param integer        $requiredMask
       
   193      * @param EntryInterface $ace
       
   194      * @return Boolean
       
   195      */
       
   196     private function isAceApplicable($requiredMask, EntryInterface $ace)
       
   197     {
       
   198         $strategy = $ace->getStrategy();
       
   199         if (self::ALL === $strategy) {
       
   200             return $requiredMask === ($ace->getMask() & $requiredMask);
       
   201         } else if (self::ANY === $strategy) {
       
   202             return 0 !== ($ace->getMask() & $requiredMask);
       
   203         } else if (self::EQUAL === $strategy) {
       
   204             return $requiredMask === $ace->getMask();
       
   205         }
       
   206 
       
   207         throw new \RuntimeException(sprintf('The strategy "%s" is not supported.', $strategy));
       
   208     }
       
   209 }