PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/Controller/Component/AclComponent.php

https://bitbucket.org/udeshika/fake_twitter
PHP | 675 lines | 281 code | 69 blank | 325 comment | 62 complexity | e84c8c69e65a7a9ccf377fd475e20be5 MD5 | raw file
  1. <?php
  2. /**
  3. * Access Control List factory class.
  4. *
  5. * Permissions system.
  6. *
  7. * PHP 5
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package Cake.Controller.Component
  18. * @since CakePHP(tm) v 0.10.0.1076
  19. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  20. */
  21. App::uses('Component', 'Controller');
  22. /**
  23. * Access Control List factory class.
  24. *
  25. * Uses a strategy pattern to allow custom ACL implementations to be used with the same component interface.
  26. * You can define by changing `Configure::write('Acl.classname', 'DbAcl');` in your core.php. Concrete ACL
  27. * implementations should extend `AclBase` and implement the methods it defines.
  28. *
  29. * @package Cake.Controller.Component
  30. * @link http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html
  31. */
  32. class AclComponent extends Component {
  33. /**
  34. * Instance of an ACL class
  35. *
  36. * @var AclInterface
  37. */
  38. protected $_Instance = null;
  39. /**
  40. * Aro object.
  41. *
  42. * @var string
  43. */
  44. public $Aro;
  45. /**
  46. * Aco object
  47. *
  48. * @var string
  49. */
  50. public $Aco;
  51. /**
  52. * Constructor. Will return an instance of the correct ACL class as defined in `Configure::read('Acl.classname')`
  53. *
  54. * @param ComponentCollection $collection
  55. * @param array $settings
  56. * @throws CakeException when Acl.classname could not be loaded.
  57. */
  58. public function __construct(ComponentCollection $collection, $settings = array()) {
  59. parent::__construct($collection, $settings);
  60. $name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
  61. if (!class_exists($name)) {
  62. if (App::import('Component', $name)) {
  63. list($plugin, $name) = pluginSplit($name);
  64. $name .= 'Component';
  65. } else {
  66. throw new CakeException(__d('cake_dev', 'Could not find %s.', $name));
  67. }
  68. }
  69. $this->adapter($name);
  70. }
  71. /**
  72. * Sets or gets the Adapter object currently in the AclComponent.
  73. *
  74. * `$this->Acl->adapter();` will get the current adapter class while
  75. * `$this->Acl->adapter($obj);` will set the adapter class
  76. *
  77. * Will call the initialize method on the adapter if setting a new one.
  78. *
  79. * @param mixed $adapter Instance of AclBase or a string name of the class to use. (optional)
  80. * @return mixed either null, or instance of AclBase
  81. * @throws CakeException when the given class is not an AclBase
  82. */
  83. public function adapter($adapter = null) {
  84. if ($adapter) {
  85. if (is_string($adapter)) {
  86. $adapter = new $adapter();
  87. }
  88. if (!$adapter instanceof AclInterface) {
  89. throw new CakeException(__d('cake_dev', 'AclComponent adapters must implement AclInterface'));
  90. }
  91. $this->_Instance = $adapter;
  92. $this->_Instance->initialize($this);
  93. return;
  94. }
  95. return $this->_Instance;
  96. }
  97. /**
  98. * Pass-thru function for ACL check instance. Check methods
  99. * are used to check whether or not an ARO can access an ACO
  100. *
  101. * @param string $aro ARO The requesting object identifier.
  102. * @param string $aco ACO The controlled object identifier.
  103. * @param string $action Action (defaults to *)
  104. * @return boolean Success
  105. */
  106. public function check($aro, $aco, $action = "*") {
  107. return $this->_Instance->check($aro, $aco, $action);
  108. }
  109. /**
  110. * Pass-thru function for ACL allow instance. Allow methods
  111. * are used to grant an ARO access to an ACO.
  112. *
  113. * @param string $aro ARO The requesting object identifier.
  114. * @param string $aco ACO The controlled object identifier.
  115. * @param string $action Action (defaults to *)
  116. * @return boolean Success
  117. */
  118. public function allow($aro, $aco, $action = "*") {
  119. return $this->_Instance->allow($aro, $aco, $action);
  120. }
  121. /**
  122. * Pass-thru function for ACL deny instance. Deny methods
  123. * are used to remove permission from an ARO to access an ACO.
  124. *
  125. * @param string $aro ARO The requesting object identifier.
  126. * @param string $aco ACO The controlled object identifier.
  127. * @param string $action Action (defaults to *)
  128. * @return boolean Success
  129. */
  130. public function deny($aro, $aco, $action = "*") {
  131. return $this->_Instance->deny($aro, $aco, $action);
  132. }
  133. /**
  134. * Pass-thru function for ACL inherit instance. Inherit methods
  135. * modify the permission for an ARO to be that of its parent object.
  136. *
  137. * @param string $aro ARO The requesting object identifier.
  138. * @param string $aco ACO The controlled object identifier.
  139. * @param string $action Action (defaults to *)
  140. * @return boolean Success
  141. */
  142. public function inherit($aro, $aco, $action = "*") {
  143. return $this->_Instance->inherit($aro, $aco, $action);
  144. }
  145. /**
  146. * Pass-thru function for ACL grant instance. An alias for AclComponent::allow()
  147. *
  148. * @param string $aro ARO The requesting object identifier.
  149. * @param string $aco ACO The controlled object identifier.
  150. * @param string $action Action (defaults to *)
  151. * @return boolean Success
  152. * @deprecated
  153. */
  154. public function grant($aro, $aco, $action = "*") {
  155. trigger_error(__d('cake_dev', 'AclComponent::grant() is deprecated, use allow() instead'), E_USER_WARNING);
  156. return $this->_Instance->allow($aro, $aco, $action);
  157. }
  158. /**
  159. * Pass-thru function for ACL grant instance. An alias for AclComponent::deny()
  160. *
  161. * @param string $aro ARO The requesting object identifier.
  162. * @param string $aco ACO The controlled object identifier.
  163. * @param string $action Action (defaults to *)
  164. * @return boolean Success
  165. * @deprecated
  166. */
  167. public function revoke($aro, $aco, $action = "*") {
  168. trigger_error(__d('cake_dev', 'AclComponent::revoke() is deprecated, use deny() instead'), E_USER_WARNING);
  169. return $this->_Instance->deny($aro, $aco, $action);
  170. }
  171. }
  172. /**
  173. * Access Control List interface.
  174. * Implementing classes are used by AclComponent to perform ACL checks in Cake.
  175. *
  176. * @package Cake.Controller.Component
  177. */
  178. interface AclInterface {
  179. /**
  180. * Empty method to be overridden in subclasses
  181. *
  182. * @param string $aro ARO The requesting object identifier.
  183. * @param string $aco ACO The controlled object identifier.
  184. * @param string $action Action (defaults to *)
  185. */
  186. public function check($aro, $aco, $action = "*");
  187. /**
  188. * Allow methods are used to grant an ARO access to an ACO.
  189. *
  190. * @param string $aro ARO The requesting object identifier.
  191. * @param string $aco ACO The controlled object identifier.
  192. * @param string $action Action (defaults to *)
  193. * @return boolean Success
  194. */
  195. public function allow($aro, $aco, $action = "*");
  196. /**
  197. * Deny methods are used to remove permission from an ARO to access an ACO.
  198. *
  199. * @param string $aro ARO The requesting object identifier.
  200. * @param string $aco ACO The controlled object identifier.
  201. * @param string $action Action (defaults to *)
  202. * @return boolean Success
  203. */
  204. public function deny($aro, $aco, $action = "*");
  205. /**
  206. * Inherit methods modify the permission for an ARO to be that of its parent object.
  207. *
  208. * @param string $aro ARO The requesting object identifier.
  209. * @param string $aco ACO The controlled object identifier.
  210. * @param string $action Action (defaults to *)
  211. * @return boolean Success
  212. */
  213. public function inherit($aro, $aco, $action = "*");
  214. /**
  215. * Initialization method for the Acl implementation
  216. *
  217. * @param AclComponent $component
  218. */
  219. public function initialize($component);
  220. }
  221. /**
  222. * DbAcl implements an ACL control system in the database. ARO's and ACO's are
  223. * structured into trees and a linking table is used to define permissions. You
  224. * can install the schema for DbAcl with the Schema Shell.
  225. *
  226. * `$aco` and `$aro` parameters can be slash delimited paths to tree nodes.
  227. *
  228. * eg. `controllers/Users/edit`
  229. *
  230. * Would point to a tree structure like
  231. *
  232. * {{{
  233. * controllers
  234. * Users
  235. * edit
  236. * }}}
  237. *
  238. * @package Cake.Controller.Component
  239. */
  240. class DbAcl extends Object implements AclInterface {
  241. /**
  242. * Constructor
  243. *
  244. */
  245. public function __construct() {
  246. parent::__construct();
  247. App::uses('AclNode', 'Model');
  248. $this->Aro = ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
  249. $this->Aco = ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
  250. }
  251. /**
  252. * Initializes the containing component and sets the Aro/Aco objects to it.
  253. *
  254. * @param AclComponent $component
  255. * @return void
  256. */
  257. public function initialize($component) {
  258. $component->Aro = $this->Aro;
  259. $component->Aco = $this->Aco;
  260. }
  261. /**
  262. * Checks if the given $aro has access to action $action in $aco
  263. *
  264. * @param string $aro ARO The requesting object identifier.
  265. * @param string $aco ACO The controlled object identifier.
  266. * @param string $action Action (defaults to *)
  267. * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
  268. * @link http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#checking-permissions-the-acl-component
  269. */
  270. public function check($aro, $aco, $action = "*") {
  271. if ($aro == null || $aco == null) {
  272. return false;
  273. }
  274. $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
  275. $aroPath = $this->Aro->node($aro);
  276. $acoPath = $this->Aco->node($aco);
  277. if (empty($aroPath) || empty($acoPath)) {
  278. trigger_error(__d('cake_dev', "DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
  279. return false;
  280. }
  281. if ($acoPath == null || $acoPath == array()) {
  282. trigger_error(__d('cake_dev', "DbAcl::check() - Failed ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
  283. return false;
  284. }
  285. if ($action != '*' && !in_array('_' . $action, $permKeys)) {
  286. trigger_error(__d('cake_dev', "ACO permissions key %s does not exist in DbAcl::check()", $action), E_USER_NOTICE);
  287. return false;
  288. }
  289. $inherited = array();
  290. $acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id');
  291. $count = count($aroPath);
  292. for ($i = 0 ; $i < $count; $i++) {
  293. $permAlias = $this->Aro->Permission->alias;
  294. $perms = $this->Aro->Permission->find('all', array(
  295. 'conditions' => array(
  296. "{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
  297. "{$permAlias}.aco_id" => $acoIDs
  298. ),
  299. 'order' => array($this->Aco->alias . '.lft' => 'desc'),
  300. 'recursive' => 0
  301. ));
  302. if (empty($perms)) {
  303. continue;
  304. } else {
  305. $perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
  306. foreach ($perms as $perm) {
  307. if ($action == '*') {
  308. foreach ($permKeys as $key) {
  309. if (!empty($perm)) {
  310. if ($perm[$key] == -1) {
  311. return false;
  312. } elseif ($perm[$key] == 1) {
  313. $inherited[$key] = 1;
  314. }
  315. }
  316. }
  317. if (count($inherited) === count($permKeys)) {
  318. return true;
  319. }
  320. } else {
  321. switch ($perm['_' . $action]) {
  322. case -1:
  323. return false;
  324. case 0:
  325. continue;
  326. break;
  327. case 1:
  328. return true;
  329. break;
  330. }
  331. }
  332. }
  333. }
  334. }
  335. return false;
  336. }
  337. /**
  338. * Allow $aro to have access to action $actions in $aco
  339. *
  340. * @param string $aro ARO The requesting object identifier.
  341. * @param string $aco ACO The controlled object identifier.
  342. * @param string $actions Action (defaults to *)
  343. * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
  344. * @return boolean Success
  345. * @link http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
  346. */
  347. public function allow($aro, $aco, $actions = "*", $value = 1) {
  348. $perms = $this->getAclLink($aro, $aco);
  349. $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
  350. $save = array();
  351. if ($perms == false) {
  352. trigger_error(__d('cake_dev', 'DbAcl::allow() - Invalid node'), E_USER_WARNING);
  353. return false;
  354. }
  355. if (isset($perms[0])) {
  356. $save = $perms[0][$this->Aro->Permission->alias];
  357. }
  358. if ($actions == "*") {
  359. $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
  360. $save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
  361. } else {
  362. if (!is_array($actions)) {
  363. $actions = array('_' . $actions);
  364. }
  365. if (is_array($actions)) {
  366. foreach ($actions as $action) {
  367. if ($action{0} != '_') {
  368. $action = '_' . $action;
  369. }
  370. if (in_array($action, $permKeys)) {
  371. $save[$action] = $value;
  372. }
  373. }
  374. }
  375. }
  376. list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
  377. if ($perms['link'] != null && !empty($perms['link'])) {
  378. $save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
  379. } else {
  380. unset($save['id']);
  381. $this->Aro->Permission->id = null;
  382. }
  383. return ($this->Aro->Permission->save($save) !== false);
  384. }
  385. /**
  386. * Deny access for $aro to action $action in $aco
  387. *
  388. * @param string $aro ARO The requesting object identifier.
  389. * @param string $aco ACO The controlled object identifier.
  390. * @param string $action Action (defaults to *)
  391. * @return boolean Success
  392. * @link http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
  393. */
  394. public function deny($aro, $aco, $action = "*") {
  395. return $this->allow($aro, $aco, $action, -1);
  396. }
  397. /**
  398. * Let access for $aro to action $action in $aco be inherited
  399. *
  400. * @param string $aro ARO The requesting object identifier.
  401. * @param string $aco ACO The controlled object identifier.
  402. * @param string $action Action (defaults to *)
  403. * @return boolean Success
  404. */
  405. public function inherit($aro, $aco, $action = "*") {
  406. return $this->allow($aro, $aco, $action, 0);
  407. }
  408. /**
  409. * Allow $aro to have access to action $actions in $aco
  410. *
  411. * @param string $aro ARO The requesting object identifier.
  412. * @param string $aco ACO The controlled object identifier.
  413. * @param string $action Action (defaults to *)
  414. * @return boolean Success
  415. * @see allow()
  416. */
  417. public function grant($aro, $aco, $action = "*") {
  418. return $this->allow($aro, $aco, $action);
  419. }
  420. /**
  421. * Deny access for $aro to action $action in $aco
  422. *
  423. * @param string $aro ARO The requesting object identifier.
  424. * @param string $aco ACO The controlled object identifier.
  425. * @param string $action Action (defaults to *)
  426. * @return boolean Success
  427. * @see deny()
  428. */
  429. public function revoke($aro, $aco, $action = "*") {
  430. return $this->deny($aro, $aco, $action);
  431. }
  432. /**
  433. * Get an array of access-control links between the given Aro and Aco
  434. *
  435. * @param string $aro ARO The requesting object identifier.
  436. * @param string $aco ACO The controlled object identifier.
  437. * @return array Indexed array with: 'aro', 'aco' and 'link'
  438. */
  439. public function getAclLink($aro, $aco) {
  440. $obj = array();
  441. $obj['Aro'] = $this->Aro->node($aro);
  442. $obj['Aco'] = $this->Aco->node($aco);
  443. if (empty($obj['Aro']) || empty($obj['Aco'])) {
  444. return false;
  445. }
  446. return array(
  447. 'aro' => Set::extract($obj, 'Aro.0.' . $this->Aro->alias . '.id'),
  448. 'aco' => Set::extract($obj, 'Aco.0.' . $this->Aco->alias . '.id'),
  449. 'link' => $this->Aro->Permission->find('all', array('conditions' => array(
  450. $this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.' . $this->Aro->alias . '.id'),
  451. $this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.' . $this->Aco->alias . '.id')
  452. )))
  453. );
  454. }
  455. /**
  456. * Get the keys used in an ACO
  457. *
  458. * @param array $keys Permission model info
  459. * @return array ACO keys
  460. */
  461. protected function _getAcoKeys($keys) {
  462. $newKeys = array();
  463. $keys = array_keys($keys);
  464. foreach ($keys as $key) {
  465. if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
  466. $newKeys[] = $key;
  467. }
  468. }
  469. return $newKeys;
  470. }
  471. }
  472. /**
  473. * IniAcl implements an access control system using an INI file. An example
  474. * of the ini file used can be found in /config/acl.ini.php.
  475. *
  476. * @package Cake.Controller.Component
  477. */
  478. class IniAcl extends Object implements AclInterface {
  479. /**
  480. * Array with configuration, parsed from ini file
  481. *
  482. * @var array
  483. */
  484. public $config = null;
  485. /**
  486. * The Set::classicExtract() path to the user/aro identifier in the
  487. * acl.ini file. This path will be used to extract the string
  488. * representation of a user used in the ini file.
  489. *
  490. * @var string
  491. */
  492. public $userPath = 'User.username';
  493. /**
  494. * Initialize method
  495. *
  496. * @param AclBase $component
  497. * @return void
  498. */
  499. public function initialize($component) {
  500. }
  501. /**
  502. * No op method, allow cannot be done with IniAcl
  503. *
  504. * @param string $aro ARO The requesting object identifier.
  505. * @param string $aco ACO The controlled object identifier.
  506. * @param string $action Action (defaults to *)
  507. * @return boolean Success
  508. */
  509. public function allow($aro, $aco, $action = "*") {
  510. }
  511. /**
  512. * No op method, deny cannot be done with IniAcl
  513. *
  514. * @param string $aro ARO The requesting object identifier.
  515. * @param string $aco ACO The controlled object identifier.
  516. * @param string $action Action (defaults to *)
  517. * @return boolean Success
  518. */
  519. public function deny($aro, $aco, $action = "*") {
  520. }
  521. /**
  522. * No op method, inherit cannot be done with IniAcl
  523. *
  524. * @param string $aro ARO The requesting object identifier.
  525. * @param string $aco ACO The controlled object identifier.
  526. * @param string $action Action (defaults to *)
  527. * @return boolean Success
  528. */
  529. public function inherit($aro, $aco, $action = "*") {
  530. }
  531. /**
  532. * Main ACL check function. Checks to see if the ARO (access request object) has access to the
  533. * ACO (access control object).Looks at the acl.ini.php file for permissions
  534. * (see instructions in /config/acl.ini.php).
  535. *
  536. * @param string $aro ARO
  537. * @param string $aco ACO
  538. * @param string $aco_action Action
  539. * @return boolean Success
  540. */
  541. public function check($aro, $aco, $aco_action = null) {
  542. if ($this->config == null) {
  543. $this->config = $this->readConfigFile(APP . 'Config' . DS . 'acl.ini.php');
  544. }
  545. $aclConfig = $this->config;
  546. if (is_array($aro)) {
  547. $aro = Set::classicExtract($aro, $this->userPath);
  548. }
  549. if (isset($aclConfig[$aro]['deny'])) {
  550. $userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
  551. if (array_search($aco, $userDenies)) {
  552. return false;
  553. }
  554. }
  555. if (isset($aclConfig[$aro]['allow'])) {
  556. $userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
  557. if (array_search($aco, $userAllows)) {
  558. return true;
  559. }
  560. }
  561. if (isset($aclConfig[$aro]['groups'])) {
  562. $userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
  563. foreach ($userGroups as $group) {
  564. if (array_key_exists($group, $aclConfig)) {
  565. if (isset($aclConfig[$group]['deny'])) {
  566. $groupDenies = $this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
  567. if (array_search($aco, $groupDenies)) {
  568. return false;
  569. }
  570. }
  571. if (isset($aclConfig[$group]['allow'])) {
  572. $groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
  573. if (array_search($aco, $groupAllows)) {
  574. return true;
  575. }
  576. }
  577. }
  578. }
  579. }
  580. return false;
  581. }
  582. /**
  583. * Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
  584. *
  585. * @param string $filename File
  586. * @return array INI section structure
  587. */
  588. public function readConfigFile($filename) {
  589. App::uses('IniReader', 'Configure');
  590. $iniFile = new IniReader(dirname($filename) . DS);
  591. return $iniFile->read(basename($filename));
  592. }
  593. /**
  594. * Removes trailing spaces on all array elements (to prepare for searching)
  595. *
  596. * @param array $array Array to trim
  597. * @return array Trimmed array
  598. */
  599. public function arrayTrim($array) {
  600. foreach ($array as $key => $value) {
  601. $array[$key] = trim($value);
  602. }
  603. array_unshift($array, "");
  604. return $array;
  605. }
  606. }