comparison cake/libs/controller/components/acl.php @ 0:261e66bd5a0c

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