Acl.php 41 KB

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