2
0

Acl.php 41 KB

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