File indexing completed on 2024-04-28 06:00:00

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