Inheritances
Files
Overview
FRAMES
NO FRAMES

Acl.php source code

Contents of file Acl.php
1 <?php
2
/**
3  * Zend Framework
4  *
5  * LICENSE
6  *
7  * This source file is subject to the new BSD license that is bundled
8  * with this package in the file LICENSE.txt.
9  * It is also available through the world-wide-web at this URL:
10  * http://framework.zend.com/license/new-bsd
11  * If you did not receive a copy of the license and are unable to
12  * obtain it through the world-wide-web, please send an email
13  * to license@zend.com so we can send you a copy immediately.
14  *
15  * @category   Zend
16  * @package    Zend_Acl
17  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
19  * @version    $Id: Acl.php 17515 2009-08-10 13:48:44Z ralph $
20  */
21
22
23 /**
24  * @see Zend_Acl_Resource_Interface
25  */
26
require_once 'Zend/Acl/Resource/Interface.php';
27
28
29
/**
30  * @see Zend_Acl_Role_Registry
31  */
32
require_once 'Zend/Acl/Role/Registry.php';
33
34
35
/**
36  * @see Zend_Acl_Assert_Interface
37  */
38
require_once 'Zend/Acl/Assert/Interface.php';
39
40
41
/**
42  * @category   Zend
43  * @package    Zend_Acl
44  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
45  * @license    http://framework.zend.com/license/new-bsd     New BSD License
46  */
47
class Zend_Acl
48
{
49     
/**
50      * Rule type: allow
51      */
52     
const TYPE_ALLOW 'TYPE_ALLOW';
53
54     
/**
55      * Rule type: deny
56      */
57     
const TYPE_DENY  'TYPE_DENY';
58
59     
/**
60      * Rule operation: add
61      */
62     
const OP_ADD 'OP_ADD';
63
64     
/**
65      * Rule operation: remove
66      */
67     
const OP_REMOVE 'OP_REMOVE';
68
69     
/**
70      * Role registry
71      *
72      * @var Zend_Acl_Role_Registry
73      */
74     
protected $_roleRegistry null;
75
76     
/**
77      * Resource tree
78      *
79      * @var array
80      */
81     
protected $_resources = array();
82
83     
/**
84      * @var Zend_Acl_Role_Interface
85      */
86     
protected $_isAllowedRole     null;
87     
88     
/**
89      * @var Zend_Acl_Resource_Interface
90      */
91     
protected $_isAllowedResource null;
92     
93     
/**
94      * ACL rules; whitelist (deny everything to all) by default
95      *
96      * @var array
97      */
98     
protected $_rules = array(
99         
'allResources' => array(
100             
'allRoles' => array(
101                 
'allPrivileges' => array(
102                     
'type'   => self::TYPE_DENY,
103                     
'assert' => null
104                     
),
105                 
'byPrivilegeId' => array()
106                 ),
107             
'byRoleId' => array()
108             ),
109         
'byResourceId' => array()
110         );
111
112     
/**
113      * Adds a Role having an identifier unique to the registry
114      *
115      * The $parents parameter may be a reference to, or the string identifier for,
116      * a Role existing in the registry, or $parents may be passed as an array of
117      * these - mixing string identifiers and objects is ok - to indicate the Roles
118      * from which the newly added Role will directly inherit.
119      *
120      * In order to resolve potential ambiguities with conflicting rules inherited
121      * from different parents, the most recently added parent takes precedence over
122      * parents that were previously added. In other words, the first parent added
123      * will have the least priority, and the last parent added will have the
124      * highest priority.
125      *
126      * @param  Zend_Acl_Role_Interface              $role
127      * @param  Zend_Acl_Role_Interface|string|array $parents
128      * @uses   Zend_Acl_Role_Registry::add()
129      * @return Zend_Acl Provides a fluent interface
130      */
131     
public function addRole($role$parents null)
132     {
133         if (
is_string($role)) {
134             
$role = new Zend_Acl_Role($role);
135         }
136         
137         if (!
$role instanceof Zend_Acl_Role_Interface) {
138             require_once 
'Zend/Acl/Exception.php';
139             throw new 
Zend_Acl_Exception('addRole() expects $role to be of type Zend_Acl_Role_Interface');
140         }
141         
142         
143         
$this->_getRoleRegistry()->add($role$parents);
144
145         return 
$this;
146     }
147
148     
/**
149      * Returns the identified Role
150      *
151      * The $role parameter can either be a Role or Role identifier.
152      *
153      * @param  Zend_Acl_Role_Interface|string $role
154      * @uses   Zend_Acl_Role_Registry::get()
155      * @return Zend_Acl_Role_Interface
156      */
157     
public function getRole($role)
158     {
159         return 
$this->_getRoleRegistry()->get($role);
160     }
161
162     
/**
163      * Returns true if and only if the Role exists in the registry
164      *
165      * The $role parameter can either be a Role or a Role identifier.
166      *
167      * @param  Zend_Acl_Role_Interface|string $role
168      * @uses   Zend_Acl_Role_Registry::has()
169      * @return boolean
170      */
171     
public function hasRole($role)
172     {
173         return 
$this->_getRoleRegistry()->has($role);
174     }
175
176     
/**
177      * Returns true if and only if $role inherits from $inherit
178      *
179      * Both parameters may be either a Role or a Role identifier. If
180      * $onlyParents is true, then $role must inherit directly from
181      * $inherit in order to return true. By default, this method looks
182      * through the entire inheritance DAG to determine whether $role
183      * inherits from $inherit through its ancestor Roles.
184      *
185      * @param  Zend_Acl_Role_Interface|string $role
186      * @param  Zend_Acl_Role_Interface|string $inherit
187      * @param  boolean                        $onlyParents
188      * @uses   Zend_Acl_Role_Registry::inherits()
189      * @return boolean
190      */
191     
public function inheritsRole($role$inherit$onlyParents false)
192     {
193         return 
$this->_getRoleRegistry()->inherits($role$inherit$onlyParents);
194     }
195
196     
/**
197      * Removes the Role from the registry
198      *
199      * The $role parameter can either be a Role or a Role identifier.
200      *
201      * @param  Zend_Acl_Role_Interface|string $role
202      * @uses   Zend_Acl_Role_Registry::remove()
203      * @return Zend_Acl Provides a fluent interface
204      */
205     
public function removeRole($role)
206     {
207         
$this->_getRoleRegistry()->remove($role);
208
209         if (
$role instanceof Zend_Acl_Role_Interface) {
210             
$roleId $role->getRoleId();
211         } else {
212             
$roleId $role;
213         }
214
215         foreach (
$this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) {
216             if (
$roleId === $roleIdCurrent) {
217                 unset(
$this->_rules['allResources']['byRoleId'][$roleIdCurrent]);
218             }
219         }
220         foreach (
$this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
221             foreach (
$visitor['byRoleId'] as $roleIdCurrent => $rules) {
222                 if (
$roleId === $roleIdCurrent) {
223                     unset(
$this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
224                 }
225             }
226         }
227
228         return 
$this;
229     }
230
231     
/**
232      * Removes all Roles from the registry
233      *
234      * @uses   Zend_Acl_Role_Registry::removeAll()
235      * @return Zend_Acl Provides a fluent interface
236      */
237     
public function removeRoleAll()
238     {
239         
$this->_getRoleRegistry()->removeAll();
240
241         foreach (
$this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) {
242             unset(
$this->_rules['allResources']['byRoleId'][$roleIdCurrent]);
243         }
244         foreach (
$this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
245             foreach (
$visitor['byRoleId'] as $roleIdCurrent => $rules) {
246                 unset(
$this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
247             }
248         }
249
250         return 
$this;
251     }
252
253     
/**
254      * Adds a Resource having an identifier unique to the ACL
255      *
256      * The $parent parameter may be a reference to, or the string identifier for,
257      * the existing Resource from which the newly added Resource will inherit.
258      *
259      * @param  Zend_Acl_Resource_Interface|string $resource
260      * @param  Zend_Acl_Resource_Interface|string $parent
261      * @throws Zend_Acl_Exception
262      * @return Zend_Acl Provides a fluent interface
263      */
264     
public function addResource($resource$parent null)
265     {
266         if (
is_string($resource)) {
267             
$resource = new Zend_Acl_Resource($resource);
268         }
269         
270         if (!
$resource instanceof Zend_Acl_Resource_Interface) {
271             require_once 
'Zend/Acl/Exception.php';
272             throw new 
Zend_Acl_Exception('addResource() expects $resource to be of type Zend_Acl_Resource_Interface');
273         }
274         
275         
$resourceId $resource->getResourceId();
276
277         if (
$this->has($resourceId)) {
278             require_once 
'Zend/Acl/Exception.php';
279             throw new 
Zend_Acl_Exception("Resource id '$resourceId' already exists in the ACL");
280         }
281
282         
$resourceParent null;
283
284         if (
null !== $parent) {
285             try {
286                 if (
$parent instanceof Zend_Acl_Resource_Interface) {
287                     
$resourceParentId $parent->getResourceId();
288                 } else {
289                     
$resourceParentId $parent;
290                 }
291                 
$resourceParent $this->get($resourceParentId);
292             } catch (
Zend_Acl_Exception $e) {
293                 throw new 
Zend_Acl_Exception("Parent Resource id '$resourceParentId' does not exist");
294             }
295             
$this->_resources[$resourceParentId]['children'][$resourceId] = $resource;
296         }
297
298         
$this->_resources[$resourceId] = array(
299             
'instance' => $resource,
300             
'parent'   => $resourceParent,
301             
'children' => array()
302             );
303
304         return 
$this;
305     }
306     
307     
/**
308      * Adds a Resource having an identifier unique to the ACL
309      *
310      * The $parent parameter may be a reference to, or the string identifier for,
311      * the existing Resource from which the newly added Resource will inherit.
312      *
313      * @deprecated in version 1.9.1 and will be available till 2.0.  New code
314      *             should use addResource() instead.
315      * 
316      * @param  Zend_Acl_Resource_Interface        $resource
317      * @param  Zend_Acl_Resource_Interface|string $parent
318      * @throws Zend_Acl_Exception
319      * @return Zend_Acl Provides a fluent interface
320      */
321     
public function add(Zend_Acl_Resource_Interface $resource$parent null)
322     {
323         return 
$this->addResource($resource$parent);
324     }
325
326     
/**
327      * Returns the identified Resource
328      *
329      * The $resource parameter can either be a Resource or a Resource identifier.
330      *
331      * @param  Zend_Acl_Resource_Interface|string $resource
332      * @throws Zend_Acl_Exception
333      * @return Zend_Acl_Resource_Interface
334      */
335     
public function get($resource)
336     {
337         if (
$resource instanceof Zend_Acl_Resource_Interface) {
338             
$resourceId $resource->getResourceId();
339         } else {
340             
$resourceId = (string) $resource;
341         }
342
343         if (!
$this->has($resource)) {
344             require_once 
'Zend/Acl/Exception.php';
345             throw new 
Zend_Acl_Exception("Resource '$resourceId' not found");
346         }
347
348         return 
$this->_resources[$resourceId]['instance'];
349     }
350
351     
/**
352      * Returns true if and only if the Resource exists in the ACL
353      *
354      * The $resource parameter can either be a Resource or a Resource identifier.
355      *
356      * @param  Zend_Acl_Resource_Interface|string $resource
357      * @return boolean
358      */
359     
public function has($resource)
360     {
361         if (
$resource instanceof Zend_Acl_Resource_Interface) {
362             
$resourceId $resource->getResourceId();
363         } else {
364             
$resourceId = (string) $resource;
365         }
366
367         return isset(
$this->_resources[$resourceId]);
368     }
369
370     
/**
371      * Returns true if and only if $resource inherits from $inherit
372      *
373      * Both parameters may be either a Resource or a Resource identifier. If
374      * $onlyParent is true, then $resource must inherit directly from
375      * $inherit in order to return true. By default, this method looks
376      * through the entire inheritance tree to determine whether $resource
377      * inherits from $inherit through its ancestor Resources.
378      *
379      * @param  Zend_Acl_Resource_Interface|string $resource
380      * @param  Zend_Acl_Resource_Interface|string $inherit
381      * @param  boolean                            $onlyParent
382      * @throws Zend_Acl_Resource_Registry_Exception
383      * @return boolean
384      */
385     
public function inherits($resource$inherit$onlyParent false)
386     {
387         try {
388             
$resourceId     $this->get($resource)->getResourceId();
389             
$inheritId $this->get($inherit)->getResourceId();
390         } catch (
Zend_Acl_Exception $e) {
391             throw 
$e;
392         }
393
394         if (
null !== $this->_resources[$resourceId]['parent']) {
395             
$parentId $this->_resources[$resourceId]['parent']->getResourceId();
396             if (
$inheritId === $parentId) {
397                 return 
true;
398             } else if (
$onlyParent) {
399                 return 
false;
400             }
401         } else {
402             return 
false;
403         }
404
405         while (
null !== $this->_resources[$parentId]['parent']) {
406             
$parentId $this->_resources[$parentId]['parent']->getResourceId();
407             if (
$inheritId === $parentId) {
408                 return 
true;
409             }
410         }
411
412         return 
false;
413     }
414
415     
/**
416      * Removes a Resource and all of its children
417      *
418      * The $resource parameter can either be a Resource or a Resource identifier.
419      *
420      * @param  Zend_Acl_Resource_Interface|string $resource
421      * @throws Zend_Acl_Exception
422      * @return Zend_Acl Provides a fluent interface
423      */
424     
public function remove($resource)
425     {
426         try {
427             
$resourceId $this->get($resource)->getResourceId();
428         } catch (
Zend_Acl_Exception $e) {
429             throw 
$e;
430         }
431
432         
$resourcesRemoved = array($resourceId);
433         if (
null !== ($resourceParent $this->_resources[$resourceId]['parent'])) {
434             unset(
$this->_resources[$resourceParent->getResourceId()]['children'][$resourceId]);
435         }
436         foreach (
$this->_resources[$resourceId]['children'] as $childId => $child) {
437             
$this->remove($childId);
438             
$resourcesRemoved[] = $childId;
439         }
440
441         foreach (
$resourcesRemoved as $resourceIdRemoved) {
442             foreach (
$this->_rules['byResourceId'] as $resourceIdCurrent => $rules) {
443                 if (
$resourceIdRemoved === $resourceIdCurrent) {
444                     unset(
$this->_rules['byResourceId'][$resourceIdCurrent]);
445                 }
446             }
447         }
448
449         unset(
$this->_resources[$resourceId]);
450
451         return 
$this;
452     }
453
454     
/**
455      * Removes all Resources
456      *
457      * @return Zend_Acl Provides a fluent interface
458      */
459     
public function removeAll()
460     {
461         foreach (
$this->_resources as $resourceId => $resource) {
462             foreach (
$this->_rules['byResourceId'] as $resourceIdCurrent => $rules) {
463                 if (
$resourceId === $resourceIdCurrent) {
464                     unset(
$this->_rules['byResourceId'][$resourceIdCurrent]);
465                 }
466             }
467         }
468
469         
$this->_resources = array();
470
471         return 
$this;
472     }
473
474     
/**
475      * Adds an "allow" rule to the ACL
476      *
477      * @param  Zend_Acl_Role_Interface|string|array     $roles
478      * @param  Zend_Acl_Resource_Interface|string|array $resources
479      * @param  string|array                             $privileges
480      * @param  Zend_Acl_Assert_Interface                $assert
481      * @uses   Zend_Acl::setRule()
482      * @return Zend_Acl Provides a fluent interface
483      */
484     
public function allow($roles null$resources null$privileges nullZend_Acl_Assert_Interface $assert null)
485     {
486         return 
$this->setRule(self::OP_ADDself::TYPE_ALLOW$roles$resources$privileges$assert);
487     }
488
489     
/**
490      * Adds a "deny" rule to the ACL
491      *
492      * @param  Zend_Acl_Role_Interface|string|array     $roles
493      * @param  Zend_Acl_Resource_Interface|string|array $resources
494      * @param  string|array                             $privileges
495      * @param  Zend_Acl_Assert_Interface                $assert
496      * @uses   Zend_Acl::setRule()
497      * @return Zend_Acl Provides a fluent interface
498      */
499     
public function deny($roles null$resources null$privileges nullZend_Acl_Assert_Interface $assert null)
500     {
501         return 
$this->setRule(self::OP_ADDself::TYPE_DENY$roles$resources$privileges$assert);
502     }
503
504     
/**
505      * Removes "allow" permissions from the ACL
506      *
507      * @param  Zend_Acl_Role_Interface|string|array     $roles
508      * @param  Zend_Acl_Resource_Interface|string|array $resources
509      * @param  string|array                             $privileges
510      * @uses   Zend_Acl::setRule()
511      * @return Zend_Acl Provides a fluent interface
512      */
513     
public function removeAllow($roles null$resources null$privileges null)
514     {
515         return 
$this->setRule(self::OP_REMOVEself::TYPE_ALLOW$roles$resources$privileges);
516     }
517
518     
/**
519      * Removes "deny" restrictions from the ACL
520      *
521      * @param  Zend_Acl_Role_Interface|string|array     $roles
522      * @param  Zend_Acl_Resource_Interface|string|array $resources
523      * @param  string|array                             $privileges
524      * @uses   Zend_Acl::setRule()
525      * @return Zend_Acl Provides a fluent interface
526      */
527     
public function removeDeny($roles null$resources null$privileges null)
528     {
529         return 
$this->setRule(self::OP_REMOVEself::TYPE_DENY$roles$resources$privileges);
530     }
531
532     
/**
533      * Performs operations on ACL rules
534      *
535      * The $operation parameter may be either OP_ADD or OP_REMOVE, depending on whether the
536      * user wants to add or remove a rule, respectively:
537      *
538      * OP_ADD specifics:
539      *
540      *      A rule is added that would allow one or more Roles access to [certain $privileges
541      *      upon] the specified Resource(s).
542      *
543      * OP_REMOVE specifics:
544      *
545      *      The rule is removed only in the context of the given Roles, Resources, and privileges.
546      *      Existing rules to which the remove operation does not apply would remain in the
547      *      ACL.
548      *
549      * The $type parameter may be either TYPE_ALLOW or TYPE_DENY, depending on whether the
550      * rule is intended to allow or deny permission, respectively.
551      *
552      * The $roles and $resources parameters may be references to, or the string identifiers for,
553      * existing Resources/Roles, or they may be passed as arrays of these - mixing string identifiers
554      * and objects is ok - to indicate the Resources and Roles to which the rule applies. If either
555      * $roles or $resources is null, then the rule applies to all Roles or all Resources, respectively.
556      * Both may be null in order to work with the default rule of the ACL.
557      *
558      * The $privileges parameter may be used to further specify that the rule applies only
559      * to certain privileges upon the Resource(s) in question. This may be specified to be a single
560      * privilege with a string, and multiple privileges may be specified as an array of strings.
561      *
562      * If $assert is provided, then its assert() method must return true in order for
563      * the rule to apply. If $assert is provided with $roles, $resources, and $privileges all
564      * equal to null, then a rule having a type of:
565      *
566      *      TYPE_ALLOW will imply a type of TYPE_DENY, and
567      *
568      *      TYPE_DENY will imply a type of TYPE_ALLOW
569      *
570      * when the rule's assertion fails. This is because the ACL needs to provide expected
571      * behavior when an assertion upon the default ACL rule fails.
572      *
573      * @param  string                                   $operation
574      * @param  string                                   $type
575      * @param  Zend_Acl_Role_Interface|string|array     $roles
576      * @param  Zend_Acl_Resource_Interface|string|array $resources
577      * @param  string|array                             $privileges
578      * @param  Zend_Acl_Assert_Interface                $assert
579      * @throws Zend_Acl_Exception
580      * @uses   Zend_Acl_Role_Registry::get()
581      * @uses   Zend_Acl::get()
582      * @return Zend_Acl Provides a fluent interface
583      */
584     
public function setRule($operation$type$roles null$resources null$privileges null,
585                             
Zend_Acl_Assert_Interface $assert null)
586     {
587         
// ensure that the rule type is valid; normalize input to uppercase
588         
$type strtoupper($type);
589         if (
self::TYPE_ALLOW !== $type && self::TYPE_DENY !== $type) {
590             require_once 
'Zend/Acl/Exception.php';
591             throw new 
Zend_Acl_Exception("Unsupported rule type; must be either '" self::TYPE_ALLOW "' or '"
592                                        
self::TYPE_DENY "'");
593         }
594
595         
// ensure that all specified Roles exist; normalize input to array of Role objects or null
596         
if (!is_array($roles)) {
597             
$roles = array($roles);
598         } else if (
=== count($roles)) {
599             
$roles = array(null);
600         }
601         
$rolesTemp $roles;
602         
$roles = array();
603         foreach (
$rolesTemp as $role) {
604             if (
null !== $role) {
605                 
$roles[] = $this->_getRoleRegistry()->get($role);
606             } else {
607                 
$roles[] = null;
608             }
609         }
610         unset(
$rolesTemp);
611
612         
// ensure that all specified Resources exist; normalize input to array of Resource objects or null
613         
if (!is_array($resources)) {
614             
$resources = array($resources);
615         } else if (
=== count($resources)) {
616             
$resources = array(null);
617         }
618         
$resourcesTemp $resources;
619         
$resources = array();
620         foreach (
$resourcesTemp as $resource) {
621             if (
null !== $resource) {
622                 
$resources[] = $this->get($resource);
623             } else {
624                 
$resources[] = null;
625             }
626         }
627         unset(
$resourcesTemp);
628
629         
// normalize privileges to array
630         
if (null === $privileges) {
631             
$privileges = array();
632         } else if (!
is_array($privileges)) {
633             
$privileges = array($privileges);
634         }
635
636         switch (
$operation) {
637
638             
// add to the rules
639             
case self::OP_ADD:
640                 foreach (
$resources as $resource) {
641                     foreach (
$roles as $role) {
642                         
$rules =& $this->_getRules($resource$roletrue);
643                         if (
=== count($privileges)) {
644                             
$rules['allPrivileges']['type']   = $type;
645                             
$rules['allPrivileges']['assert'] = $assert;
646                             if (!isset(
$rules['byPrivilegeId'])) {
647                                 
$rules['byPrivilegeId'] = array();
648                             }
649                         } else {
650                             foreach (
$privileges as $privilege) {
651                                 
$rules['byPrivilegeId'][$privilege]['type']   = $type;
652                                 
$rules['byPrivilegeId'][$privilege]['assert'] = $assert;
653                             }
654                         }
655                     }
656                 }
657                 break;
658
659             
// remove from the rules
660             
case self::OP_REMOVE:
661                 foreach (
$resources as $resource) {
662                     foreach (
$roles as $role) {
663                         
$rules =& $this->_getRules($resource$role);
664                         if (
null === $rules) {
665                             continue;
666                         }
667                         if (
=== count($privileges)) {
668                             if (
null === $resource && null === $role) {
669                                 if (
$type === $rules['allPrivileges']['type']) {
670                                     
$rules = array(
671                                         
'allPrivileges' => array(
672                                             
'type'   => self::TYPE_DENY,
673                                             
'assert' => null
674                                             
),
675                                         
'byPrivilegeId' => array()
676                                         );
677                                 }
678                                 continue;
679                             }
680                             if (
$type === $rules['allPrivileges']['type']) {
681                                 unset(
$rules['allPrivileges']);
682                             }
683                         } else {
684                             foreach (
$privileges as $privilege) {
685                                 if (isset(
$rules['byPrivilegeId'][$privilege]) &&
686                                     
$type === $rules['byPrivilegeId'][$privilege]['type']) {
687                                     unset(
$rules['byPrivilegeId'][$privilege]);
688                                 }
689                             }
690                         }
691                     }
692                 }
693                 break;
694
695             default:
696                 require_once 
'Zend/Acl/Exception.php';
697                 throw new 
Zend_Acl_Exception("Unsupported operation; must be either '" self::OP_ADD "' or '"
698                                            
self::OP_REMOVE "'");
699         }
700
701         return 
$this;
702     }
703
704     
/**
705      * Returns true if and only if the Role has access to the Resource
706      *
707      * The $role and $resource parameters may be references to, or the string identifiers for,
708      * an existing Resource and Role combination.
709      *
710      * If either $role or $resource is null, then the query applies to all Roles or all Resources,
711      * respectively. Both may be null to query whether the ACL has a "blacklist" rule
712      * (allow everything to all). By default, Zend_Acl creates a "whitelist" rule (deny
713      * everything to all), and this method would return false unless this default has
714      * been overridden (i.e., by executing $acl->allow()).
715      *
716      * If a $privilege is not provided, then this method returns false if and only if the
717      * Role is denied access to at least one privilege upon the Resource. In other words, this
718      * method returns true if and only if the Role is allowed all privileges on the Resource.
719      *
720      * This method checks Role inheritance using a depth-first traversal of the Role registry.
721      * The highest priority parent (i.e., the parent most recently added) is checked first,
722      * and its respective parents are checked similarly before the lower-priority parents of
723      * the Role are checked.
724      *
725      * @param  Zend_Acl_Role_Interface|string     $role
726      * @param  Zend_Acl_Resource_Interface|string $resource
727      * @param  string                             $privilege
728      * @uses   Zend_Acl::get()
729      * @uses   Zend_Acl_Role_Registry::get()
730      * @return boolean
731      */
732     
public function isAllowed($role null$resource null$privilege null)
733     {
734         
// reset role & resource to null
735         
$this->_isAllowedRole $this->_isAllowedResource null;
736         
737         if (
null !== $role) {
738             
// keep track of originally called role
739             
$this->_isAllowedRole $role;
740             
$role $this->_getRoleRegistry()->get($role);
741             if (!
$this->_isAllowedRole instanceof Zend_Acl_Role_Interface) {
742                 
$this->_isAllowedRole $role;
743             }
744         }
745
746         if (
null !== $resource) {
747             
// keep track of originally called resource
748             
$this->_isAllowedResource $resource;
749             
$resource $this->get($resource);
750             if (!
$this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) {
751                 
$this->_isAllowedResource $resource;
752             }
753         }
754
755         if (
null === $privilege) {
756             
// query on all privileges
757             
do {
758                 
// depth-first search on $role if it is not 'allRoles' pseudo-parent
759                 
if (null !== $role && null !== ($result $this->_roleDFSAllPrivileges($role$resource$privilege))) {
760                     return 
$result;
761                 }
762
763                 
// look for rule on 'allRoles' psuedo-parent
764                 
if (null !== ($rules $this->_getRules($resourcenull))) {
765                     foreach (
$rules['byPrivilegeId'] as $privilege => $rule) {
766                         if (
self::TYPE_DENY === ($ruleTypeOnePrivilege $this->_getRuleType($resourcenull$privilege))) {
767                             return 
false;
768                         }
769                     }
770                     if (
null !== ($ruleTypeAllPrivileges $this->_getRuleType($resourcenullnull))) {
771                         return 
self::TYPE_ALLOW === $ruleTypeAllPrivileges;
772                     }
773                 }
774
775                 
// try next Resource
776                 
$resource $this->_resources[$resource->getResourceId()]['parent'];
777
778             } while (
true); // loop terminates at 'allResources' pseudo-parent
779         
} else {
780             
// query on one privilege
781             
do {
782                 
// depth-first search on $role if it is not 'allRoles' pseudo-parent
783                 
if (null !== $role && null !== ($result $this->_roleDFSOnePrivilege($role$resource$privilege))) {
784                     return 
$result;
785                 }
786
787                 
// look for rule on 'allRoles' pseudo-parent
788                 
if (null !== ($ruleType $this->_getRuleType($resourcenull$privilege))) {
789                     return 
self::TYPE_ALLOW === $ruleType;
790                 } else if (
null !== ($ruleTypeAllPrivileges $this->_getRuleType($resourcenullnull))) {
791                     return 
self::TYPE_ALLOW === $ruleTypeAllPrivileges;
792                 }
793
794                 
// try next Resource
795                 
$resource $this->_resources[$resource->getResourceId()]['parent'];
796
797             } while (
true); // loop terminates at 'allResources' pseudo-parent
798         
}
799     }
800
801     
/**
802      * Returns the Role registry for this ACL
803      *
804      * If no Role registry has been created yet, a new default Role registry
805      * is created and returned.
806      *
807      * @return Zend_Acl_Role_Registry
808      */
809     
protected function _getRoleRegistry()
810     {
811         if (
null === $this->_roleRegistry) {
812             
$this->_roleRegistry = new Zend_Acl_Role_Registry();
813         }
814         return 
$this->_roleRegistry;
815     }
816
817     
/**
818      * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule
819      * allowing/denying $role access to all privileges upon $resource
820      *
821      * This method returns true if a rule is found and allows access. If a rule exists and denies access,
822      * then this method returns false. If no applicable rule is found, then this method returns null.
823      *
824      * @param  Zend_Acl_Role_Interface     $role
825      * @param  Zend_Acl_Resource_Interface $resource
826      * @return boolean|null
827      */
828     
protected function _roleDFSAllPrivileges(Zend_Acl_Role_Interface $roleZend_Acl_Resource_Interface $resource null)
829     {
830         
$dfs = array(
831             
'visited' => array(),
832             
'stack'   => array()
833             );
834
835         if (
null !== ($result $this->_roleDFSVisitAllPrivileges($role$resource$dfs))) {
836             return 
$result;
837         }
838
839         while (
null !== ($role array_pop($dfs['stack']))) {
840             if (!isset(
$dfs['visited'][$role->getRoleId()])) {
841                 if (
null !== ($result $this->_roleDFSVisitAllPrivileges($role$resource$dfs))) {
842                     return 
$result;
843                 }
844             }
845         }
846
847         return 
null;
848     }
849
850     
/**
851      * Visits an $role in order to look for a rule allowing/denying $role access to all privileges upon $resource
852      *
853      * This method returns true if a rule is found and allows access. If a rule exists and denies access,
854      * then this method returns false. If no applicable rule is found, then this method returns null.
855      *
856      * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
857      *
858      * @param  Zend_Acl_Role_Interface     $role
859      * @param  Zend_Acl_Resource_Interface $resource
860      * @param  array                  $dfs
861      * @return boolean|null
862      * @throws Zend_Acl_Exception
863      */
864     
protected function _roleDFSVisitAllPrivileges(Zend_Acl_Role_Interface $roleZend_Acl_Resource_Interface $resource null,
865                                                  &
$dfs null)
866     {
867         if (
null === $dfs) {
868             
/**
869              * @see Zend_Acl_Exception
870              */
871             
require_once 'Zend/Acl/Exception.php';
872             throw new 
Zend_Acl_Exception('$dfs parameter may not be null');
873         }
874
875         if (
null !== ($rules $this->_getRules($resource$role))) {
876             foreach (
$rules['byPrivilegeId'] as $privilege => $rule) {
877                 if (
self::TYPE_DENY === ($ruleTypeOnePrivilege $this->_getRuleType($resource$role$privilege))) {
878                     return 
false;
879                 }
880             }
881             if (
null !== ($ruleTypeAllPrivileges $this->_getRuleType($resource$rolenull))) {
882                 return 
self::TYPE_ALLOW === $ruleTypeAllPrivileges;
883             }
884         }
885
886         
$dfs['visited'][$role->getRoleId()] = true;
887         foreach (
$this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) {
888             
$dfs['stack'][] = $roleParent;
889         }
890
891         return 
null;
892     }
893
894     
/**
895      * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule
896      * allowing/denying $role access to a $privilege upon $resource
897      *
898      * This method returns true if a rule is found and allows access. If a rule exists and denies access,
899      * then this method returns false. If no applicable rule is found, then this method returns null.
900      *
901      * @param  Zend_Acl_Role_Interface     $role
902      * @param  Zend_Acl_Resource_Interface $resource
903      * @param  string                      $privilege
904      * @return boolean|null
905      * @throws Zend_Acl_Exception
906      */
907     
protected function _roleDFSOnePrivilege(Zend_Acl_Role_Interface $roleZend_Acl_Resource_Interface $resource null,
908                                             
$privilege null)
909     {
910         if (
null === $privilege) {
911             
/**
912              * @see Zend_Acl_Exception
913              */
914             
require_once 'Zend/Acl/Exception.php';
915             throw new 
Zend_Acl_Exception('$privilege parameter may not be null');
916         }
917
918         
$dfs = array(
919             
'visited' => array(),
920             
'stack'   => array()
921             );
922
923         if (
null !== ($result $this->_roleDFSVisitOnePrivilege($role$resource$privilege$dfs))) {
924             return 
$result;
925         }
926
927         while (
null !== ($role array_pop($dfs['stack']))) {
928             if (!isset(
$dfs['visited'][$role->getRoleId()])) {
929                 if (
null !== ($result $this->_roleDFSVisitOnePrivilege($role$resource$privilege$dfs))) {
930                     return 
$result;
931                 }
932             }
933         }
934
935         return 
null;
936     }
937
938     
/**
939      * Visits an $role in order to look for a rule allowing/denying $role access to a $privilege upon $resource
940      *
941      * This method returns true if a rule is found and allows access. If a rule exists and denies access,
942      * then this method returns false. If no applicable rule is found, then this method returns null.
943      *
944      * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
945      *
946      * @param  Zend_Acl_Role_Interface     $role
947      * @param  Zend_Acl_Resource_Interface $resource
948      * @param  string                      $privilege
949      * @param  array                       $dfs
950      * @return boolean|null
951      * @throws Zend_Acl_Exception
952      */
953     
protected function _roleDFSVisitOnePrivilege(Zend_Acl_Role_Interface $roleZend_Acl_Resource_Interface $resource null,
954                                                 
$privilege null, &$dfs null)
955     {
956         if (
null === $privilege) {
957             
/**
958              * @see Zend_Acl_Exception
959              */
960             
require_once 'Zend/Acl/Exception.php';
961             throw new 
Zend_Acl_Exception('$privilege parameter may not be null');
962         }
963
964         if (
null === $dfs) {
965             
/**
966              * @see Zend_Acl_Exception
967              */
968             
require_once 'Zend/Acl/Exception.php';
969             throw new 
Zend_Acl_Exception('$dfs parameter may not be null');
970         }
971
972         if (
null !== ($ruleTypeOnePrivilege $this->_getRuleType($resource$role$privilege))) {
973             return 
self::TYPE_ALLOW === $ruleTypeOnePrivilege;
974         } else if (
null !== ($ruleTypeAllPrivileges $this->_getRuleType($resource$rolenull))) {
975             return 
self::TYPE_ALLOW === $ruleTypeAllPrivileges;
976         }
977
978         
$dfs['visited'][$role->getRoleId()] = true;
979         foreach (
$this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) {
980             
$dfs['stack'][] = $roleParent;
981         }
982
983         return 
null;
984     }
985
986     
/**
987      * Returns the rule type associated with the specified Resource, Role, and privilege
988      * combination.
989      *
990      * If a rule does not exist or its attached assertion fails, which means that
991      * the rule is not applicable, then this method returns null. Otherwise, the
992      * rule type applies and is returned as either TYPE_ALLOW or TYPE_DENY.
993      *
994      * If $resource or $role is null, then this means that the rule must apply to
995      * all Resources or Roles, respectively.
996      *
997      * If $privilege is null, then the rule must apply to all privileges.
998      *
999      * If all three parameters are null, then the default ACL rule type is returned,
1000      * based on whether its assertion method passes.
1001      *
1002      * @param  Zend_Acl_Resource_Interface $resource
1003      * @param  Zend_Acl_Role_Interface     $role
1004      * @param  string                      $privilege
1005      * @return string|null
1006      */
1007     
protected function _getRuleType(Zend_Acl_Resource_Interface $resource nullZend_Acl_Role_Interface $role null,
1008                                     
$privilege null)
1009     {
1010         
// get the rules for the $resource and $role
1011         
if (null === ($rules $this->_getRules($resource$role))) {
1012             return 
null;
1013         }
1014
1015         
// follow $privilege
1016         
if (null === $privilege) {
1017             if (isset(
$rules['allPrivileges'])) {
1018                 
$rule $rules['allPrivileges'];
1019             } else {
1020                 return 
null;
1021             }
1022         } else if (!isset(
$rules['byPrivilegeId'][$privilege])) {
1023             return 
null;
1024         } else {
1025             
$rule $rules['byPrivilegeId'][$privilege];
1026         }
1027
1028         
// check assertion first
1029         
if ($rule['assert']) {
1030             
$assertion $rule['assert'];
1031             
$assertionValue $assertion->assert(
1032                 
$this,
1033                 (
$this->_isAllowedRole instanceof Zend_Acl_Role_Interface) ? $this->_isAllowedRole $role,
1034                 (
$this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) ? $this->_isAllowedResource $resource,
1035                 
$privilege
1036                 
);
1037         } 
1038         
1039         if (
null === $rule['assert'] || $assertionValue) {
1040             return 
$rule['type'];
1041         } else if (
null !== $resource || null !== $role || null !== $privilege) {
1042             return 
null;
1043         } else if (
self::TYPE_ALLOW === $rule['type']) {
1044             return 
self::TYPE_DENY;
1045         } else {
1046             return 
self::TYPE_ALLOW;
1047         }
1048     }
1049
1050     
/**
1051      * Returns the rules associated with a Resource and a Role, or null if no such rules exist
1052      *
1053      * If either $resource or $role is null, this means that the rules returned are for all Resources or all Roles,
1054      * respectively. Both can be null to return the default rule set for all Resources and all Roles.
1055      *
1056      * If the $create parameter is true, then a rule set is first created and then returned to the caller.
1057      *
1058      * @param  Zend_Acl_Resource_Interface $resource
1059      * @param  Zend_Acl_Role_Interface     $role
1060      * @param  boolean                     $create
1061      * @return array|null
1062      */
1063     
protected function &_getRules(Zend_Acl_Resource_Interface $resource nullZend_Acl_Role_Interface $role null,
1064                                   
$create false)
1065     {
1066         
// create a reference to null
1067         
$null null;
1068         
$nullRef =& $null;
1069
1070         
// follow $resource
1071         
do {
1072             if (
null === $resource) {
1073                 
$visitor =& $this->_rules['allResources'];
1074                 break;
1075             }
1076             
$resourceId $resource->getResourceId();
1077             if (!isset(
$this->_rules['byResourceId'][$resourceId])) {
1078                 if (!
$create) {
1079                     return 
$nullRef;
1080                 }
1081                 
$this->_rules['byResourceId'][$resourceId] = array();
1082             }
1083             
$visitor =& $this->_rules['byResourceId'][$resourceId];
1084         } while (
false);
1085
1086
1087         
// follow $role
1088         
if (null === $role) {
1089             if (!isset(
$visitor['allRoles'])) {
1090                 if (!
$create) {
1091                     return 
$nullRef;
1092                 }
1093                 
$visitor['allRoles']['byPrivilegeId'] = array();
1094             }
1095             return 
$visitor['allRoles'];
1096         }
1097         
$roleId $role->getRoleId();
1098         if (!isset(
$visitor['byRoleId'][$roleId])) {
1099             if (!
$create) {
1100                 return 
$nullRef;
1101             }
1102             
$visitor['byRoleId'][$roleId]['byPrivilegeId'] = array();
1103         }
1104         return 
$visitor['byRoleId'][$roleId];
1105     }
1106
1107 }
1108