File indexing completed on 2025-01-26 05:30:06
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Acl 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 0023 /** 0024 * @see Zend_Acl_Resource_Interface 0025 */ 0026 // require_once 'Zend/Acl/Resource/Interface.php'; 0027 0028 0029 /** 0030 * @see Zend_Acl_Role_Registry 0031 */ 0032 // require_once 'Zend/Acl/Role/Registry.php'; 0033 0034 0035 /** 0036 * @see Zend_Acl_Assert_Interface 0037 */ 0038 // require_once 'Zend/Acl/Assert/Interface.php'; 0039 0040 0041 /** 0042 * @see Zend_Acl_Role 0043 */ 0044 // require_once 'Zend/Acl/Role.php'; 0045 0046 0047 /** 0048 * @see Zend_Acl_Resource 0049 */ 0050 // require_once 'Zend/Acl/Resource.php'; 0051 0052 0053 /** 0054 * @category Zend 0055 * @package Zend_Acl 0056 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0057 * @license http://framework.zend.com/license/new-bsd New BSD License 0058 */ 0059 class Zend_Acl 0060 { 0061 /** 0062 * Rule type: allow 0063 */ 0064 const TYPE_ALLOW = 'TYPE_ALLOW'; 0065 0066 /** 0067 * Rule type: deny 0068 */ 0069 const TYPE_DENY = 'TYPE_DENY'; 0070 0071 /** 0072 * Rule operation: add 0073 */ 0074 const OP_ADD = 'OP_ADD'; 0075 0076 /** 0077 * Rule operation: remove 0078 */ 0079 const OP_REMOVE = 'OP_REMOVE'; 0080 0081 /** 0082 * Role registry 0083 * 0084 * @var Zend_Acl_Role_Registry 0085 */ 0086 protected $_roleRegistry = null; 0087 0088 /** 0089 * Resource tree 0090 * 0091 * @var array 0092 */ 0093 protected $_resources = array(); 0094 0095 /** 0096 * @var Zend_Acl_Role_Interface 0097 */ 0098 protected $_isAllowedRole = null; 0099 0100 /** 0101 * @var Zend_Acl_Resource_Interface 0102 */ 0103 protected $_isAllowedResource = null; 0104 0105 /** 0106 * @var String 0107 */ 0108 protected $_isAllowedPrivilege = null; 0109 0110 /** 0111 * ACL rules; whitelist (deny everything to all) by default 0112 * 0113 * @var array 0114 */ 0115 protected $_rules = array( 0116 'allResources' => array( 0117 'allRoles' => array( 0118 'allPrivileges' => array( 0119 'type' => self::TYPE_DENY, 0120 'assert' => null 0121 ), 0122 'byPrivilegeId' => array() 0123 ), 0124 'byRoleId' => array() 0125 ), 0126 'byResourceId' => array() 0127 ); 0128 0129 /** 0130 * Adds a Role having an identifier unique to the registry 0131 * 0132 * The $parents parameter may be a reference to, or the string identifier for, 0133 * a Role existing in the registry, or $parents may be passed as an array of 0134 * these - mixing string identifiers and objects is ok - to indicate the Roles 0135 * from which the newly added Role will directly inherit. 0136 * 0137 * In order to resolve potential ambiguities with conflicting rules inherited 0138 * from different parents, the most recently added parent takes precedence over 0139 * parents that were previously added. In other words, the first parent added 0140 * will have the least priority, and the last parent added will have the 0141 * highest priority. 0142 * 0143 * @param Zend_Acl_Role_Interface|string $role 0144 * @param Zend_Acl_Role_Interface|string|array $parents 0145 * @uses Zend_Acl_Role_Registry::add() 0146 * @return Zend_Acl Provides a fluent interface 0147 */ 0148 public function addRole($role, $parents = null) 0149 { 0150 if (is_string($role)) { 0151 $role = new Zend_Acl_Role($role); 0152 } 0153 0154 if (!$role instanceof Zend_Acl_Role_Interface) { 0155 // require_once 'Zend/Acl/Exception.php'; 0156 throw new Zend_Acl_Exception('addRole() expects $role to be of type Zend_Acl_Role_Interface'); 0157 } 0158 0159 0160 $this->_getRoleRegistry()->add($role, $parents); 0161 0162 return $this; 0163 } 0164 0165 /** 0166 * Returns the identified Role 0167 * 0168 * The $role parameter can either be a Role or Role identifier. 0169 * 0170 * @param Zend_Acl_Role_Interface|string $role 0171 * @uses Zend_Acl_Role_Registry::get() 0172 * @return Zend_Acl_Role_Interface 0173 */ 0174 public function getRole($role) 0175 { 0176 return $this->_getRoleRegistry()->get($role); 0177 } 0178 0179 /** 0180 * Returns true if and only if the Role exists in the registry 0181 * 0182 * The $role parameter can either be a Role or a Role identifier. 0183 * 0184 * @param Zend_Acl_Role_Interface|string $role 0185 * @uses Zend_Acl_Role_Registry::has() 0186 * @return boolean 0187 */ 0188 public function hasRole($role) 0189 { 0190 return $this->_getRoleRegistry()->has($role); 0191 } 0192 0193 /** 0194 * Returns true if and only if $role inherits from $inherit 0195 * 0196 * Both parameters may be either a Role or a Role identifier. If 0197 * $onlyParents is true, then $role must inherit directly from 0198 * $inherit in order to return true. By default, this method looks 0199 * through the entire inheritance DAG to determine whether $role 0200 * inherits from $inherit through its ancestor Roles. 0201 * 0202 * @param Zend_Acl_Role_Interface|string $role 0203 * @param Zend_Acl_Role_Interface|string $inherit 0204 * @param boolean $onlyParents 0205 * @uses Zend_Acl_Role_Registry::inherits() 0206 * @return boolean 0207 */ 0208 public function inheritsRole($role, $inherit, $onlyParents = false) 0209 { 0210 return $this->_getRoleRegistry()->inherits($role, $inherit, $onlyParents); 0211 } 0212 0213 /** 0214 * Removes the Role from the registry 0215 * 0216 * The $role parameter can either be a Role or a Role identifier. 0217 * 0218 * @param Zend_Acl_Role_Interface|string $role 0219 * @uses Zend_Acl_Role_Registry::remove() 0220 * @return Zend_Acl Provides a fluent interface 0221 */ 0222 public function removeRole($role) 0223 { 0224 $this->_getRoleRegistry()->remove($role); 0225 0226 if ($role instanceof Zend_Acl_Role_Interface) { 0227 $roleId = $role->getRoleId(); 0228 } else { 0229 $roleId = $role; 0230 } 0231 0232 foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) { 0233 if ($roleId === $roleIdCurrent) { 0234 unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]); 0235 } 0236 } 0237 foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) { 0238 if (array_key_exists('byRoleId', $visitor)) { 0239 foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) { 0240 if ($roleId === $roleIdCurrent) { 0241 unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]); 0242 } 0243 } 0244 } 0245 } 0246 0247 return $this; 0248 } 0249 0250 /** 0251 * Removes all Roles from the registry 0252 * 0253 * @uses Zend_Acl_Role_Registry::removeAll() 0254 * @return Zend_Acl Provides a fluent interface 0255 */ 0256 public function removeRoleAll() 0257 { 0258 $this->_getRoleRegistry()->removeAll(); 0259 0260 foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) { 0261 unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]); 0262 } 0263 foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) { 0264 foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) { 0265 unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]); 0266 } 0267 } 0268 0269 return $this; 0270 } 0271 0272 /** 0273 * Adds a Resource having an identifier unique to the ACL 0274 * 0275 * The $parent parameter may be a reference to, or the string identifier for, 0276 * the existing Resource from which the newly added Resource will inherit. 0277 * 0278 * @param Zend_Acl_Resource_Interface|string $resource 0279 * @param Zend_Acl_Resource_Interface|string $parent 0280 * @throws Zend_Acl_Exception 0281 * @return Zend_Acl Provides a fluent interface 0282 */ 0283 public function addResource($resource, $parent = null) 0284 { 0285 if (is_string($resource)) { 0286 $resource = new Zend_Acl_Resource($resource); 0287 } 0288 0289 if (!$resource instanceof Zend_Acl_Resource_Interface) { 0290 // require_once 'Zend/Acl/Exception.php'; 0291 throw new Zend_Acl_Exception('addResource() expects $resource to be of type Zend_Acl_Resource_Interface'); 0292 } 0293 0294 $resourceId = $resource->getResourceId(); 0295 0296 if ($this->has($resourceId)) { 0297 // require_once 'Zend/Acl/Exception.php'; 0298 throw new Zend_Acl_Exception("Resource id '$resourceId' already exists in the ACL"); 0299 } 0300 0301 $resourceParent = null; 0302 0303 if (null !== $parent) { 0304 try { 0305 if ($parent instanceof Zend_Acl_Resource_Interface) { 0306 $resourceParentId = $parent->getResourceId(); 0307 } else { 0308 $resourceParentId = $parent; 0309 } 0310 $resourceParent = $this->get($resourceParentId); 0311 } catch (Zend_Acl_Exception $e) { 0312 // require_once 'Zend/Acl/Exception.php'; 0313 throw new Zend_Acl_Exception("Parent Resource id '$resourceParentId' does not exist", 0, $e); 0314 } 0315 $this->_resources[$resourceParentId]['children'][$resourceId] = $resource; 0316 } 0317 0318 $this->_resources[$resourceId] = array( 0319 'instance' => $resource, 0320 'parent' => $resourceParent, 0321 'children' => array() 0322 ); 0323 0324 return $this; 0325 } 0326 0327 /** 0328 * Adds a Resource having an identifier unique to the ACL 0329 * 0330 * The $parent parameter may be a reference to, or the string identifier for, 0331 * the existing Resource from which the newly added Resource will inherit. 0332 * 0333 * @deprecated in version 1.9.1 and will be available till 2.0. New code 0334 * should use addResource() instead. 0335 * 0336 * @param Zend_Acl_Resource_Interface $resource 0337 * @param Zend_Acl_Resource_Interface|string $parent 0338 * @throws Zend_Acl_Exception 0339 * @return Zend_Acl Provides a fluent interface 0340 */ 0341 public function add(Zend_Acl_Resource_Interface $resource, $parent = null) 0342 { 0343 return $this->addResource($resource, $parent); 0344 } 0345 0346 /** 0347 * Returns the identified Resource 0348 * 0349 * The $resource parameter can either be a Resource or a Resource identifier. 0350 * 0351 * @param Zend_Acl_Resource_Interface|string $resource 0352 * @throws Zend_Acl_Exception 0353 * @return Zend_Acl_Resource_Interface 0354 */ 0355 public function get($resource) 0356 { 0357 if ($resource instanceof Zend_Acl_Resource_Interface) { 0358 $resourceId = $resource->getResourceId(); 0359 } else { 0360 $resourceId = (string) $resource; 0361 } 0362 0363 if (!$this->has($resource)) { 0364 // require_once 'Zend/Acl/Exception.php'; 0365 throw new Zend_Acl_Exception("Resource '$resourceId' not found"); 0366 } 0367 0368 return $this->_resources[$resourceId]['instance']; 0369 } 0370 0371 /** 0372 * Returns true if and only if the Resource exists in the ACL 0373 * 0374 * The $resource parameter can either be a Resource or a Resource identifier. 0375 * 0376 * @param Zend_Acl_Resource_Interface|string $resource 0377 * @return boolean 0378 */ 0379 public function has($resource) 0380 { 0381 if ($resource instanceof Zend_Acl_Resource_Interface) { 0382 $resourceId = $resource->getResourceId(); 0383 } else { 0384 $resourceId = (string) $resource; 0385 } 0386 0387 return isset($this->_resources[$resourceId]); 0388 } 0389 0390 /** 0391 * Returns true if and only if $resource inherits from $inherit 0392 * 0393 * Both parameters may be either a Resource or a Resource identifier. If 0394 * $onlyParent is true, then $resource must inherit directly from 0395 * $inherit in order to return true. By default, this method looks 0396 * through the entire inheritance tree to determine whether $resource 0397 * inherits from $inherit through its ancestor Resources. 0398 * 0399 * @param Zend_Acl_Resource_Interface|string $resource 0400 * @param Zend_Acl_Resource_Interface|string $inherit 0401 * @param boolean $onlyParent 0402 * @throws Zend_Acl_Resource_Registry_Exception 0403 * @return boolean 0404 */ 0405 public function inherits($resource, $inherit, $onlyParent = false) 0406 { 0407 try { 0408 $resourceId = $this->get($resource)->getResourceId(); 0409 $inheritId = $this->get($inherit)->getResourceId(); 0410 } catch (Zend_Acl_Exception $e) { 0411 // require_once 'Zend/Acl/Exception.php'; 0412 throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e); 0413 } 0414 0415 if (null !== $this->_resources[$resourceId]['parent']) { 0416 $parentId = $this->_resources[$resourceId]['parent']->getResourceId(); 0417 if ($inheritId === $parentId) { 0418 return true; 0419 } else if ($onlyParent) { 0420 return false; 0421 } 0422 } else { 0423 return false; 0424 } 0425 0426 while (null !== $this->_resources[$parentId]['parent']) { 0427 $parentId = $this->_resources[$parentId]['parent']->getResourceId(); 0428 if ($inheritId === $parentId) { 0429 return true; 0430 } 0431 } 0432 0433 return false; 0434 } 0435 0436 /** 0437 * Removes a Resource and all of its children 0438 * 0439 * The $resource parameter can either be a Resource or a Resource identifier. 0440 * 0441 * @param Zend_Acl_Resource_Interface|string $resource 0442 * @throws Zend_Acl_Exception 0443 * @return Zend_Acl Provides a fluent interface 0444 */ 0445 public function remove($resource) 0446 { 0447 try { 0448 $resourceId = $this->get($resource)->getResourceId(); 0449 } catch (Zend_Acl_Exception $e) { 0450 // require_once 'Zend/Acl/Exception.php'; 0451 throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e); 0452 } 0453 0454 $resourcesRemoved = array($resourceId); 0455 if (null !== ($resourceParent = $this->_resources[$resourceId]['parent'])) { 0456 unset($this->_resources[$resourceParent->getResourceId()]['children'][$resourceId]); 0457 } 0458 foreach ($this->_resources[$resourceId]['children'] as $childId => $child) { 0459 $this->remove($childId); 0460 $resourcesRemoved[] = $childId; 0461 } 0462 0463 foreach ($resourcesRemoved as $resourceIdRemoved) { 0464 foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) { 0465 if ($resourceIdRemoved === $resourceIdCurrent) { 0466 unset($this->_rules['byResourceId'][$resourceIdCurrent]); 0467 } 0468 } 0469 } 0470 0471 unset($this->_resources[$resourceId]); 0472 0473 return $this; 0474 } 0475 0476 /** 0477 * Removes all Resources 0478 * 0479 * @return Zend_Acl Provides a fluent interface 0480 */ 0481 public function removeAll() 0482 { 0483 foreach ($this->_resources as $resourceId => $resource) { 0484 foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) { 0485 if ($resourceId === $resourceIdCurrent) { 0486 unset($this->_rules['byResourceId'][$resourceIdCurrent]); 0487 } 0488 } 0489 } 0490 0491 $this->_resources = array(); 0492 0493 return $this; 0494 } 0495 0496 /** 0497 * Adds an "allow" rule to the ACL 0498 * 0499 * @param Zend_Acl_Role_Interface|string|array $roles 0500 * @param Zend_Acl_Resource_Interface|string|array $resources 0501 * @param string|array $privileges 0502 * @param Zend_Acl_Assert_Interface $assert 0503 * @uses Zend_Acl::setRule() 0504 * @return Zend_Acl Provides a fluent interface 0505 */ 0506 public function allow($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null) 0507 { 0508 return $this->setRule(self::OP_ADD, self::TYPE_ALLOW, $roles, $resources, $privileges, $assert); 0509 } 0510 0511 /** 0512 * Adds a "deny" rule to the ACL 0513 * 0514 * @param Zend_Acl_Role_Interface|string|array $roles 0515 * @param Zend_Acl_Resource_Interface|string|array $resources 0516 * @param string|array $privileges 0517 * @param Zend_Acl_Assert_Interface $assert 0518 * @uses Zend_Acl::setRule() 0519 * @return Zend_Acl Provides a fluent interface 0520 */ 0521 public function deny($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null) 0522 { 0523 return $this->setRule(self::OP_ADD, self::TYPE_DENY, $roles, $resources, $privileges, $assert); 0524 } 0525 0526 /** 0527 * Removes "allow" permissions from the ACL 0528 * 0529 * @param Zend_Acl_Role_Interface|string|array $roles 0530 * @param Zend_Acl_Resource_Interface|string|array $resources 0531 * @param string|array $privileges 0532 * @uses Zend_Acl::setRule() 0533 * @return Zend_Acl Provides a fluent interface 0534 */ 0535 public function removeAllow($roles = null, $resources = null, $privileges = null) 0536 { 0537 return $this->setRule(self::OP_REMOVE, self::TYPE_ALLOW, $roles, $resources, $privileges); 0538 } 0539 0540 /** 0541 * Removes "deny" restrictions from the ACL 0542 * 0543 * @param Zend_Acl_Role_Interface|string|array $roles 0544 * @param Zend_Acl_Resource_Interface|string|array $resources 0545 * @param string|array $privileges 0546 * @uses Zend_Acl::setRule() 0547 * @return Zend_Acl Provides a fluent interface 0548 */ 0549 public function removeDeny($roles = null, $resources = null, $privileges = null) 0550 { 0551 return $this->setRule(self::OP_REMOVE, self::TYPE_DENY, $roles, $resources, $privileges); 0552 } 0553 0554 /** 0555 * Performs operations on ACL rules 0556 * 0557 * The $operation parameter may be either OP_ADD or OP_REMOVE, depending on whether the 0558 * user wants to add or remove a rule, respectively: 0559 * 0560 * OP_ADD specifics: 0561 * 0562 * A rule is added that would allow one or more Roles access to [certain $privileges 0563 * upon] the specified Resource(s). 0564 * 0565 * OP_REMOVE specifics: 0566 * 0567 * The rule is removed only in the context of the given Roles, Resources, and privileges. 0568 * Existing rules to which the remove operation does not apply would remain in the 0569 * ACL. 0570 * 0571 * The $type parameter may be either TYPE_ALLOW or TYPE_DENY, depending on whether the 0572 * rule is intended to allow or deny permission, respectively. 0573 * 0574 * The $roles and $resources parameters may be references to, or the string identifiers for, 0575 * existing Resources/Roles, or they may be passed as arrays of these - mixing string identifiers 0576 * and objects is ok - to indicate the Resources and Roles to which the rule applies. If either 0577 * $roles or $resources is null, then the rule applies to all Roles or all Resources, respectively. 0578 * Both may be null in order to work with the default rule of the ACL. 0579 * 0580 * The $privileges parameter may be used to further specify that the rule applies only 0581 * to certain privileges upon the Resource(s) in question. This may be specified to be a single 0582 * privilege with a string, and multiple privileges may be specified as an array of strings. 0583 * 0584 * If $assert is provided, then its assert() method must return true in order for 0585 * the rule to apply. If $assert is provided with $roles, $resources, and $privileges all 0586 * equal to null, then a rule having a type of: 0587 * 0588 * TYPE_ALLOW will imply a type of TYPE_DENY, and 0589 * 0590 * TYPE_DENY will imply a type of TYPE_ALLOW 0591 * 0592 * when the rule's assertion fails. This is because the ACL needs to provide expected 0593 * behavior when an assertion upon the default ACL rule fails. 0594 * 0595 * @param string $operation 0596 * @param string $type 0597 * @param Zend_Acl_Role_Interface|string|array $roles 0598 * @param Zend_Acl_Resource_Interface|string|array $resources 0599 * @param string|array $privileges 0600 * @param Zend_Acl_Assert_Interface $assert 0601 * @throws Zend_Acl_Exception 0602 * @uses Zend_Acl_Role_Registry::get() 0603 * @uses Zend_Acl::get() 0604 * @return Zend_Acl Provides a fluent interface 0605 */ 0606 public function setRule($operation, $type, $roles = null, $resources = null, $privileges = null, 0607 Zend_Acl_Assert_Interface $assert = null) 0608 { 0609 // ensure that the rule type is valid; normalize input to uppercase 0610 $type = strtoupper($type); 0611 if (self::TYPE_ALLOW !== $type && self::TYPE_DENY !== $type) { 0612 // require_once 'Zend/Acl/Exception.php'; 0613 throw new Zend_Acl_Exception("Unsupported rule type; must be either '" . self::TYPE_ALLOW . "' or '" 0614 . self::TYPE_DENY . "'"); 0615 } 0616 0617 // ensure that all specified Roles exist; normalize input to array of Role objects or null 0618 if (!is_array($roles)) { 0619 $roles = array($roles); 0620 } else if (0 === count($roles)) { 0621 $roles = array(null); 0622 } 0623 $rolesTemp = $roles; 0624 $roles = array(); 0625 foreach ($rolesTemp as $role) { 0626 if (null !== $role) { 0627 $roles[] = $this->_getRoleRegistry()->get($role); 0628 } else { 0629 $roles[] = null; 0630 } 0631 } 0632 unset($rolesTemp); 0633 0634 // ensure that all specified Resources exist; normalize input to array of Resource objects or null 0635 if ($resources !== null) { 0636 if (!is_array($resources)) { 0637 $resources = array($resources); 0638 } else if (0 === count($resources)) { 0639 $resources = array(null); 0640 } 0641 $resourcesTemp = $resources; 0642 $resources = array(); 0643 foreach ($resourcesTemp as $resource) { 0644 if (null !== $resource) { 0645 $resources[] = $this->get($resource); 0646 } else { 0647 $resources[] = null; 0648 } 0649 } 0650 unset($resourcesTemp, $resource); 0651 } else { 0652 $allResources = array(); // this might be used later if resource iteration is required 0653 foreach ($this->_resources as $rTarget) { 0654 $allResources[] = $rTarget['instance']; 0655 } 0656 unset($rTarget); 0657 } 0658 0659 // normalize privileges to array 0660 if (null === $privileges) { 0661 $privileges = array(); 0662 } else if (!is_array($privileges)) { 0663 $privileges = array($privileges); 0664 } 0665 0666 switch ($operation) { 0667 0668 // add to the rules 0669 case self::OP_ADD: 0670 if ($resources !== null) { 0671 // this block will iterate the provided resources 0672 foreach ($resources as $resource) { 0673 foreach ($roles as $role) { 0674 $rules =& $this->_getRules($resource, $role, true); 0675 if (0 === count($privileges)) { 0676 $rules['allPrivileges']['type'] = $type; 0677 $rules['allPrivileges']['assert'] = $assert; 0678 if (!isset($rules['byPrivilegeId'])) { 0679 $rules['byPrivilegeId'] = array(); 0680 } 0681 } else { 0682 foreach ($privileges as $privilege) { 0683 $rules['byPrivilegeId'][$privilege]['type'] = $type; 0684 $rules['byPrivilegeId'][$privilege]['assert'] = $assert; 0685 } 0686 } 0687 } 0688 } 0689 } else { 0690 // this block will apply to all resources in a global rule 0691 foreach ($roles as $role) { 0692 $rules =& $this->_getRules(null, $role, true); 0693 if (0 === count($privileges)) { 0694 $rules['allPrivileges']['type'] = $type; 0695 $rules['allPrivileges']['assert'] = $assert; 0696 } else { 0697 foreach ($privileges as $privilege) { 0698 $rules['byPrivilegeId'][$privilege]['type'] = $type; 0699 $rules['byPrivilegeId'][$privilege]['assert'] = $assert; 0700 } 0701 } 0702 } 0703 } 0704 break; 0705 0706 // remove from the rules 0707 case self::OP_REMOVE: 0708 if ($resources !== null) { 0709 // this block will iterate the provided resources 0710 foreach ($resources as $resource) { 0711 foreach ($roles as $role) { 0712 $rules =& $this->_getRules($resource, $role); 0713 if (null === $rules) { 0714 continue; 0715 } 0716 if (0 === count($privileges)) { 0717 if (null === $resource && null === $role) { 0718 if ($type === $rules['allPrivileges']['type']) { 0719 $rules = array( 0720 'allPrivileges' => array( 0721 'type' => self::TYPE_DENY, 0722 'assert' => null 0723 ), 0724 'byPrivilegeId' => array() 0725 ); 0726 } 0727 continue; 0728 } 0729 0730 if (isset($rules['allPrivileges']['type']) && 0731 $type === $rules['allPrivileges']['type']) 0732 { 0733 unset($rules['allPrivileges']); 0734 } 0735 } else { 0736 foreach ($privileges as $privilege) { 0737 if (isset($rules['byPrivilegeId'][$privilege]) && 0738 $type === $rules['byPrivilegeId'][$privilege]['type']) 0739 { 0740 unset($rules['byPrivilegeId'][$privilege]); 0741 } 0742 } 0743 } 0744 } 0745 } 0746 } else { 0747 // this block will apply to all resources in a global rule 0748 foreach ($roles as $role) { 0749 /** 0750 * since null (all resources) was passed to this setRule() call, we need 0751 * clean up all the rules for the global allResources, as well as the indivually 0752 * set resources (per privilege as well) 0753 */ 0754 foreach (array_merge(array(null), $allResources) as $resource) { 0755 $rules =& $this->_getRules($resource, $role, true); 0756 if (null === $rules) { 0757 continue; 0758 } 0759 if (0 === count($privileges)) { 0760 if (null === $role) { 0761 if ($type === $rules['allPrivileges']['type']) { 0762 $rules = array( 0763 'allPrivileges' => array( 0764 'type' => self::TYPE_DENY, 0765 'assert' => null 0766 ), 0767 'byPrivilegeId' => array() 0768 ); 0769 } 0770 continue; 0771 } 0772 0773 if (isset($rules['allPrivileges']['type']) && $type === $rules['allPrivileges']['type']) { 0774 unset($rules['allPrivileges']); 0775 } 0776 } else { 0777 foreach ($privileges as $privilege) { 0778 if (isset($rules['byPrivilegeId'][$privilege]) && 0779 $type === $rules['byPrivilegeId'][$privilege]['type']) 0780 { 0781 unset($rules['byPrivilegeId'][$privilege]); 0782 } 0783 } 0784 } 0785 } 0786 } 0787 } 0788 break; 0789 0790 default: 0791 // require_once 'Zend/Acl/Exception.php'; 0792 throw new Zend_Acl_Exception("Unsupported operation; must be either '" . self::OP_ADD . "' or '" 0793 . self::OP_REMOVE . "'"); 0794 } 0795 0796 return $this; 0797 } 0798 0799 /** 0800 * Returns true if and only if the Role has access to the Resource 0801 * 0802 * The $role and $resource parameters may be references to, or the string identifiers for, 0803 * an existing Resource and Role combination. 0804 * 0805 * If either $role or $resource is null, then the query applies to all Roles or all Resources, 0806 * respectively. Both may be null to query whether the ACL has a "blacklist" rule 0807 * (allow everything to all). By default, Zend_Acl creates a "whitelist" rule (deny 0808 * everything to all), and this method would return false unless this default has 0809 * been overridden (i.e., by executing $acl->allow()). 0810 * 0811 * If a $privilege is not provided, then this method returns false if and only if the 0812 * Role is denied access to at least one privilege upon the Resource. In other words, this 0813 * method returns true if and only if the Role is allowed all privileges on the Resource. 0814 * 0815 * This method checks Role inheritance using a depth-first traversal of the Role registry. 0816 * The highest priority parent (i.e., the parent most recently added) is checked first, 0817 * and its respective parents are checked similarly before the lower-priority parents of 0818 * the Role are checked. 0819 * 0820 * @param Zend_Acl_Role_Interface|string $role 0821 * @param Zend_Acl_Resource_Interface|string $resource 0822 * @param string $privilege 0823 * @uses Zend_Acl::get() 0824 * @uses Zend_Acl_Role_Registry::get() 0825 * @return boolean 0826 */ 0827 public function isAllowed($role = null, $resource = null, $privilege = null) 0828 { 0829 // reset role & resource to null 0830 $this->_isAllowedRole = null; 0831 $this->_isAllowedResource = null; 0832 $this->_isAllowedPrivilege = null; 0833 0834 if (null !== $role) { 0835 // keep track of originally called role 0836 $this->_isAllowedRole = $role; 0837 $role = $this->_getRoleRegistry()->get($role); 0838 if (!$this->_isAllowedRole instanceof Zend_Acl_Role_Interface) { 0839 $this->_isAllowedRole = $role; 0840 } 0841 } 0842 0843 if (null !== $resource) { 0844 // keep track of originally called resource 0845 $this->_isAllowedResource = $resource; 0846 $resource = $this->get($resource); 0847 if (!$this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) { 0848 $this->_isAllowedResource = $resource; 0849 } 0850 } 0851 0852 if (null === $privilege) { 0853 // query on all privileges 0854 do { 0855 // depth-first search on $role if it is not 'allRoles' pseudo-parent 0856 if (null !== $role && null !== ($result = $this->_roleDFSAllPrivileges($role, $resource, $privilege))) { 0857 return $result; 0858 } 0859 0860 // look for rule on 'allRoles' psuedo-parent 0861 if (null !== ($rules = $this->_getRules($resource, null))) { 0862 foreach ($rules['byPrivilegeId'] as $privilege => $rule) { 0863 if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, null, $privilege))) { 0864 return false; 0865 } 0866 } 0867 if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) { 0868 return self::TYPE_ALLOW === $ruleTypeAllPrivileges; 0869 } 0870 } 0871 0872 // try next Resource 0873 $resource = $this->_resources[$resource->getResourceId()]['parent']; 0874 0875 } while (true); // loop terminates at 'allResources' pseudo-parent 0876 } else { 0877 $this->_isAllowedPrivilege = $privilege; 0878 // query on one privilege 0879 do { 0880 // depth-first search on $role if it is not 'allRoles' pseudo-parent 0881 if (null !== $role && null !== ($result = $this->_roleDFSOnePrivilege($role, $resource, $privilege))) { 0882 return $result; 0883 } 0884 0885 // look for rule on 'allRoles' pseudo-parent 0886 if (null !== ($ruleType = $this->_getRuleType($resource, null, $privilege))) { 0887 return self::TYPE_ALLOW === $ruleType; 0888 } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) { 0889 return self::TYPE_ALLOW === $ruleTypeAllPrivileges; 0890 } 0891 0892 // try next Resource 0893 $resource = $this->_resources[$resource->getResourceId()]['parent']; 0894 0895 } while (true); // loop terminates at 'allResources' pseudo-parent 0896 } 0897 } 0898 0899 /** 0900 * Returns the Role registry for this ACL 0901 * 0902 * If no Role registry has been created yet, a new default Role registry 0903 * is created and returned. 0904 * 0905 * @return Zend_Acl_Role_Registry 0906 */ 0907 protected function _getRoleRegistry() 0908 { 0909 if (null === $this->_roleRegistry) { 0910 $this->_roleRegistry = new Zend_Acl_Role_Registry(); 0911 } 0912 return $this->_roleRegistry; 0913 } 0914 0915 /** 0916 * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule 0917 * allowing/denying $role access to all privileges upon $resource 0918 * 0919 * This method returns true if a rule is found and allows access. If a rule exists and denies access, 0920 * then this method returns false. If no applicable rule is found, then this method returns null. 0921 * 0922 * @param Zend_Acl_Role_Interface $role 0923 * @param Zend_Acl_Resource_Interface $resource 0924 * @return boolean|null 0925 */ 0926 protected function _roleDFSAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null) 0927 { 0928 $dfs = array( 0929 'visited' => array(), 0930 'stack' => array() 0931 ); 0932 0933 if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) { 0934 return $result; 0935 } 0936 0937 while (null !== ($role = array_pop($dfs['stack']))) { 0938 if (!isset($dfs['visited'][$role->getRoleId()])) { 0939 if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) { 0940 return $result; 0941 } 0942 } 0943 } 0944 0945 return null; 0946 } 0947 0948 /** 0949 * Visits an $role in order to look for a rule allowing/denying $role access to all privileges upon $resource 0950 * 0951 * This method returns true if a rule is found and allows access. If a rule exists and denies access, 0952 * then this method returns false. If no applicable rule is found, then this method returns null. 0953 * 0954 * This method is used by the internal depth-first search algorithm and may modify the DFS data structure. 0955 * 0956 * @param Zend_Acl_Role_Interface $role 0957 * @param Zend_Acl_Resource_Interface $resource 0958 * @param array $dfs 0959 * @return boolean|null 0960 * @throws Zend_Acl_Exception 0961 */ 0962 protected function _roleDFSVisitAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, 0963 &$dfs = null) 0964 { 0965 if (null === $dfs) { 0966 /** 0967 * @see Zend_Acl_Exception 0968 */ 0969 // require_once 'Zend/Acl/Exception.php'; 0970 throw new Zend_Acl_Exception('$dfs parameter may not be null'); 0971 } 0972 0973 if (null !== ($rules = $this->_getRules($resource, $role))) { 0974 foreach ($rules['byPrivilegeId'] as $privilege => $rule) { 0975 if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) { 0976 return false; 0977 } 0978 } 0979 if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) { 0980 return self::TYPE_ALLOW === $ruleTypeAllPrivileges; 0981 } 0982 } 0983 0984 $dfs['visited'][$role->getRoleId()] = true; 0985 foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) { 0986 $dfs['stack'][] = $roleParent; 0987 } 0988 0989 return null; 0990 } 0991 0992 /** 0993 * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule 0994 * allowing/denying $role access to a $privilege upon $resource 0995 * 0996 * This method returns true if a rule is found and allows access. If a rule exists and denies access, 0997 * then this method returns false. If no applicable rule is found, then this method returns null. 0998 * 0999 * @param Zend_Acl_Role_Interface $role 1000 * @param Zend_Acl_Resource_Interface $resource 1001 * @param string $privilege 1002 * @return boolean|null 1003 * @throws Zend_Acl_Exception 1004 */ 1005 protected function _roleDFSOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, 1006 $privilege = null) 1007 { 1008 if (null === $privilege) { 1009 /** 1010 * @see Zend_Acl_Exception 1011 */ 1012 // require_once 'Zend/Acl/Exception.php'; 1013 throw new Zend_Acl_Exception('$privilege parameter may not be null'); 1014 } 1015 1016 $dfs = array( 1017 'visited' => array(), 1018 'stack' => array() 1019 ); 1020 1021 if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) { 1022 return $result; 1023 } 1024 1025 while (null !== ($role = array_pop($dfs['stack']))) { 1026 if (!isset($dfs['visited'][$role->getRoleId()])) { 1027 if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) { 1028 return $result; 1029 } 1030 } 1031 } 1032 1033 return null; 1034 } 1035 1036 /** 1037 * Visits an $role in order to look for a rule allowing/denying $role access to a $privilege upon $resource 1038 * 1039 * This method returns true if a rule is found and allows access. If a rule exists and denies access, 1040 * then this method returns false. If no applicable rule is found, then this method returns null. 1041 * 1042 * This method is used by the internal depth-first search algorithm and may modify the DFS data structure. 1043 * 1044 * @param Zend_Acl_Role_Interface $role 1045 * @param Zend_Acl_Resource_Interface $resource 1046 * @param string $privilege 1047 * @param array $dfs 1048 * @return boolean|null 1049 * @throws Zend_Acl_Exception 1050 */ 1051 protected function _roleDFSVisitOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, 1052 $privilege = null, &$dfs = null) 1053 { 1054 if (null === $privilege) { 1055 /** 1056 * @see Zend_Acl_Exception 1057 */ 1058 // require_once 'Zend/Acl/Exception.php'; 1059 throw new Zend_Acl_Exception('$privilege parameter may not be null'); 1060 } 1061 1062 if (null === $dfs) { 1063 /** 1064 * @see Zend_Acl_Exception 1065 */ 1066 // require_once 'Zend/Acl/Exception.php'; 1067 throw new Zend_Acl_Exception('$dfs parameter may not be null'); 1068 } 1069 1070 if (null !== ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) { 1071 return self::TYPE_ALLOW === $ruleTypeOnePrivilege; 1072 } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) { 1073 return self::TYPE_ALLOW === $ruleTypeAllPrivileges; 1074 } 1075 1076 $dfs['visited'][$role->getRoleId()] = true; 1077 foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) { 1078 $dfs['stack'][] = $roleParent; 1079 } 1080 1081 return null; 1082 } 1083 1084 /** 1085 * Returns the rule type associated with the specified Resource, Role, and privilege 1086 * combination. 1087 * 1088 * If a rule does not exist or its attached assertion fails, which means that 1089 * the rule is not applicable, then this method returns null. Otherwise, the 1090 * rule type applies and is returned as either TYPE_ALLOW or TYPE_DENY. 1091 * 1092 * If $resource or $role is null, then this means that the rule must apply to 1093 * all Resources or Roles, respectively. 1094 * 1095 * If $privilege is null, then the rule must apply to all privileges. 1096 * 1097 * If all three parameters are null, then the default ACL rule type is returned, 1098 * based on whether its assertion method passes. 1099 * 1100 * @param Zend_Acl_Resource_Interface $resource 1101 * @param Zend_Acl_Role_Interface $role 1102 * @param string $privilege 1103 * @return string|null 1104 */ 1105 protected function _getRuleType(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null, 1106 $privilege = null) 1107 { 1108 // get the rules for the $resource and $role 1109 if (null === ($rules = $this->_getRules($resource, $role))) { 1110 return null; 1111 } 1112 1113 // follow $privilege 1114 if (null === $privilege) { 1115 if (isset($rules['allPrivileges'])) { 1116 $rule = $rules['allPrivileges']; 1117 } else { 1118 return null; 1119 } 1120 } else if (!isset($rules['byPrivilegeId'][$privilege])) { 1121 return null; 1122 } else { 1123 $rule = $rules['byPrivilegeId'][$privilege]; 1124 } 1125 1126 // check assertion first 1127 if ($rule['assert']) { 1128 $assertion = $rule['assert']; 1129 $assertionValue = $assertion->assert( 1130 $this, 1131 ($this->_isAllowedRole instanceof Zend_Acl_Role_Interface) ? $this->_isAllowedRole : $role, 1132 ($this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) ? $this->_isAllowedResource : $resource, 1133 $this->_isAllowedPrivilege 1134 ); 1135 } 1136 1137 if (null === $rule['assert'] || $assertionValue) { 1138 return $rule['type']; 1139 } else if (null !== $resource || null !== $role || null !== $privilege) { 1140 return null; 1141 } else if (self::TYPE_ALLOW === $rule['type']) { 1142 return self::TYPE_DENY; 1143 } else { 1144 return self::TYPE_ALLOW; 1145 } 1146 } 1147 1148 /** 1149 * Returns the rules associated with a Resource and a Role, or null if no such rules exist 1150 * 1151 * If either $resource or $role is null, this means that the rules returned are for all Resources or all Roles, 1152 * respectively. Both can be null to return the default rule set for all Resources and all Roles. 1153 * 1154 * If the $create parameter is true, then a rule set is first created and then returned to the caller. 1155 * 1156 * @param Zend_Acl_Resource_Interface $resource 1157 * @param Zend_Acl_Role_Interface $role 1158 * @param boolean $create 1159 * @return array|null 1160 */ 1161 protected function &_getRules(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null, 1162 $create = false) 1163 { 1164 // create a reference to null 1165 $null = null; 1166 $nullRef =& $null; 1167 1168 // follow $resource 1169 do { 1170 if (null === $resource) { 1171 $visitor =& $this->_rules['allResources']; 1172 break; 1173 } 1174 $resourceId = $resource->getResourceId(); 1175 if (!isset($this->_rules['byResourceId'][$resourceId])) { 1176 if (!$create) { 1177 return $nullRef; 1178 } 1179 $this->_rules['byResourceId'][$resourceId] = array(); 1180 } 1181 $visitor =& $this->_rules['byResourceId'][$resourceId]; 1182 } while (false); 1183 1184 1185 // follow $role 1186 if (null === $role) { 1187 if (!isset($visitor['allRoles'])) { 1188 if (!$create) { 1189 return $nullRef; 1190 } 1191 $visitor['allRoles']['byPrivilegeId'] = array(); 1192 } 1193 return $visitor['allRoles']; 1194 } 1195 $roleId = $role->getRoleId(); 1196 if (!isset($visitor['byRoleId'][$roleId])) { 1197 if (!$create) { 1198 return $nullRef; 1199 } 1200 $visitor['byRoleId'][$roleId]['byPrivilegeId'] = array(); 1201 $visitor['byRoleId'][$roleId]['allPrivileges'] = array('type' => null, 'assert' => null); 1202 } 1203 return $visitor['byRoleId'][$roleId]; 1204 } 1205 1206 1207 /** 1208 * @return array of registered roles (Deprecated) 1209 * @deprecated Deprecated since version 1.10 (December 2009) 1210 */ 1211 public function getRegisteredRoles() 1212 { 1213 trigger_error('The method getRegisteredRoles() was deprecated as of ' 1214 . 'version 1.0, and may be removed. You\'re encouraged ' 1215 . 'to use getRoles() instead.'); 1216 1217 return $this->_getRoleRegistry()->getRoles(); 1218 } 1219 1220 /** 1221 * Returns an array of registered roles. 1222 * 1223 * Note that this method does not return instances of registered roles, 1224 * but only the role identifiers. 1225 * 1226 * @return array of registered roles 1227 */ 1228 public function getRoles() 1229 { 1230 return array_keys($this->_getRoleRegistry()->getRoles()); 1231 } 1232 1233 /** 1234 * @return array of registered resources 1235 */ 1236 public function getResources() 1237 { 1238 return array_keys($this->_resources); 1239 } 1240 1241 } 1242