CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • DbAcl
  • IniAcl
  • PhpAcl
  • PhpAco
  • PhpAro

Interfaces

  • AclInterface
  1: <?php
  2: /**
  3:  * PHP configuration based AclInterface implementation
  4:  *
  5:  * PHP 5
  6:  *
  7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9:  *
 10:  * Licensed under The MIT License
 11:  * Redistributions of files must retain the above copyright notice.
 12:  *
 13:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 14:  * @link          http://cakephp.org CakePHP(tm) Project
 15:  * @package       Cake.Controller.Component.Acl
 16:  * @since         CakePHP(tm) v 2.1
 17:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 18:  */
 19: 
 20: /**
 21:  * PhpAcl implements an access control system using a plain PHP configuration file.
 22:  * An example file can be found in app/Config/acl.php
 23:  *
 24:  * @package Cake.Controller.Component.Acl
 25:  */
 26: class PhpAcl extends Object implements AclInterface {
 27: 
 28:     const DENY = false;
 29:     const ALLOW = true;
 30: 
 31: /**
 32:  * Options:
 33:  *  - policy: determines behavior of the check method. Deny policy needs explicit allow rules, allow policy needs explicit deny rules
 34:  *  - config: absolute path to config file that contains the acl rules (@see app/Config/acl.php)
 35:  *
 36:  * @var array
 37:  */
 38:     public $options = array();
 39: 
 40: /**
 41:  * Aro Object
 42:  *
 43:  * @var PhpAro
 44:  */
 45:     public $Aro = null;
 46: 
 47: /**
 48:  * Aco Object
 49:  *
 50:  * @var PhpAco
 51:  */
 52:     public $Aco = null;
 53: 
 54: /**
 55:  * Constructor
 56:  *
 57:  * Sets a few default settings up.
 58:  */
 59:     public function __construct() {
 60:         $this->options = array(
 61:             'policy' => self::DENY,
 62:             'config' => APP . 'Config' . DS . 'acl.php',
 63:         );
 64:     }
 65: 
 66: /**
 67:  * Initialize method
 68:  *
 69:  * @param AclComponent $Component Component instance
 70:  * @return void
 71:  */
 72:     public function initialize(Component $Component) {
 73:         if (!empty($Component->settings['adapter'])) {
 74:             $this->options = array_merge($this->options, $Component->settings['adapter']);
 75:         }
 76: 
 77:         App::uses('PhpReader', 'Configure');
 78:         $Reader = new PhpReader(dirname($this->options['config']) . DS);
 79:         $config = $Reader->read(basename($this->options['config']));
 80:         $this->build($config);
 81:         $Component->Aco = $this->Aco;
 82:         $Component->Aro = $this->Aro;
 83:     }
 84: 
 85: /**
 86:  * build and setup internal ACL representation
 87:  *
 88:  * @param array $config configuration array, see docs
 89:  * @return void
 90:  * @throws AclException When required keys are missing.
 91:  */
 92:     public function build(array $config) {
 93:         if (empty($config['roles'])) {
 94:             throw new AclException(__d('cake_dev','"roles" section not found in configuration.'));
 95:         }
 96: 
 97:         if (empty($config['rules']['allow']) && empty($config['rules']['deny'])) {
 98:             throw new AclException(__d('cake_dev','Neither "allow" nor "deny" rules were provided in configuration.'));
 99:         }
100: 
101:         $rules['allow'] = !empty($config['rules']['allow']) ? $config['rules']['allow'] : array();
102:         $rules['deny'] = !empty($config['rules']['deny']) ? $config['rules']['deny'] : array();
103:         $roles = !empty($config['roles']) ? $config['roles'] : array();
104:         $map = !empty($config['map']) ? $config['map'] : array();
105:         $alias = !empty($config['alias']) ? $config['alias'] : array();
106: 
107:         $this->Aro = new PhpAro($roles, $map, $alias);
108:         $this->Aco = new PhpAco($rules);
109:     }
110: 
111: /**
112:  * No op method, allow cannot be done with PhpAcl
113:  *
114:  * @param string $aro ARO The requesting object identifier.
115:  * @param string $aco ACO The controlled object identifier.
116:  * @param string $action Action (defaults to *)
117:  * @return boolean Success
118:  */
119:     public function allow($aro, $aco, $action = "*") {
120:         return $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'allow');
121:     }
122: 
123: /**
124:  * deny ARO access to ACO
125:  *
126:  * @param string $aro ARO The requesting object identifier.
127:  * @param string $aco ACO The controlled object identifier.
128:  * @param string $action Action (defaults to *)
129:  * @return boolean Success
130:  */
131:     public function deny($aro, $aco, $action = "*") {
132:         return $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'deny');
133:     }
134: 
135: /**
136:  * No op method
137:  *
138:  * @param string $aro ARO The requesting object identifier.
139:  * @param string $aco ACO The controlled object identifier.
140:  * @param string $action Action (defaults to *)
141:  * @return boolean Success
142:  */
143:     public function inherit($aro, $aco, $action = "*") {
144:         return false;
145:     }
146: 
147: /**
148:  * Main ACL check function. Checks to see if the ARO (access request object) has access to the
149:  * ACO (access control object).
150:  *
151:  * @param string $aro ARO
152:  * @param string $aco ACO
153:  * @param string $action Action
154:  * @return boolean true if access is granted, false otherwise
155:  */
156:     public function check($aro, $aco, $action = "*") {
157:         $allow = $this->options['policy'];
158:         $prioritizedAros = $this->Aro->roles($aro);
159: 
160:         if ($action && $action != "*") {
161:             $aco .= '/' . $action;
162:         }
163: 
164:         $path = $this->Aco->path($aco);
165: 
166:         if (empty($path)) {
167:             return $allow;
168:         }
169: 
170:         foreach ($path as $depth => $node) {
171:             foreach ($prioritizedAros as $aros) {
172:                 if (!empty($node['allow'])) {
173:                     $allow = $allow || count(array_intersect($node['allow'], $aros)) > 0;
174:                 }
175: 
176:                 if (!empty($node['deny'])) {
177:                     $allow = $allow && count(array_intersect($node['deny'], $aros)) == 0;
178:                 }
179:             }
180:         }
181: 
182:         return $allow;
183:     }
184: 
185: }
186: 
187: /**
188:  * Access Control Object
189:  *
190:  */
191: class PhpAco {
192: 
193: /**
194:  * holds internal ACO representation
195:  *
196:  * @var array
197:  */
198:     protected $_tree = array();
199: 
200: /**
201:  * map modifiers for ACO paths to their respective PCRE pattern
202:  *
203:  * @var array
204:  */
205:     public static $modifiers = array(
206:         '*' => '.*',
207:     );
208: 
209:     public function __construct(array $rules = array()) {
210:         foreach (array('allow', 'deny') as $type) {
211:             if (empty($rules[$type])) {
212:                 $rules[$type] = array();
213:             }
214:         }
215: 
216:         $this->build($rules['allow'], $rules['deny']);
217:     }
218: 
219: /**
220:  * return path to the requested ACO with allow and deny rules attached on each level
221:  *
222:  * @return array
223:  */
224:     public function path($aco) {
225:         $aco = $this->resolve($aco);
226:         $path = array();
227:         $level = 0;
228:         $root = $this->_tree;
229:         $stack = array(array($root, 0));
230: 
231:         while (!empty($stack)) {
232:             list($root, $level) = array_pop($stack);
233: 
234:             if (empty($path[$level])) {
235:                 $path[$level] = array();
236:             }
237: 
238:             foreach ($root as $node => $elements) {
239:                 $pattern = '/^' . str_replace(array_keys(self::$modifiers), array_values(self::$modifiers), $node) . '$/';
240: 
241:                 if ($node == $aco[$level] || preg_match($pattern, $aco[$level])) {
242:                     // merge allow/denies with $path of current level
243:                     foreach (array('allow', 'deny') as $policy) {
244:                         if (!empty($elements[$policy])) {
245:                             if (empty($path[$level][$policy])) {
246:                                 $path[$level][$policy] = array();
247:                             }
248:                             $path[$level][$policy] = array_merge($path[$level][$policy], $elements[$policy]);
249:                         }
250:                     }
251: 
252:                     // traverse
253:                     if (!empty($elements['children']) && isset($aco[$level + 1])) {
254:                         array_push($stack, array($elements['children'], $level + 1));
255:                     }
256:                 }
257:             }
258:         }
259: 
260:         return $path;
261:     }
262: 
263: /**
264:  * allow/deny ARO access to ARO
265:  *
266:  * @return void
267:  */
268:     public function access($aro, $aco, $action, $type = 'deny') {
269:         $aco = $this->resolve($aco);
270:         $depth = count($aco);
271:         $root = $this->_tree;
272:         $tree = &$root;
273: 
274:         foreach ($aco as $i => $node) {
275:             if (!isset($tree[$node])) {
276:                 $tree[$node]  = array(
277:                     'children' => array(),
278:                 );
279:             }
280: 
281:             if ($i < $depth - 1) {
282:                 $tree = &$tree[$node]['children'];
283:             } else {
284:                 if (empty($tree[$node][$type])) {
285:                     $tree[$node][$type] = array();
286:                 }
287: 
288:                 $tree[$node][$type] = array_merge(is_array($aro) ? $aro : array($aro), $tree[$node][$type]);
289:             }
290:         }
291: 
292:         $this->_tree = &$root;
293:     }
294: 
295: /**
296:  * resolve given ACO string to a path
297:  *
298:  * @param string $aco ACO string
299:  * @return array path
300:  */
301:     public function resolve($aco) {
302:         if (is_array($aco)) {
303:             return array_map('strtolower', $aco);
304:         }
305: 
306:         // strip multiple occurences of '/'
307:         $aco = preg_replace('#/+#', '/', $aco);
308:         // make case insensitive
309:         $aco = ltrim(strtolower($aco), '/');
310:         return array_filter(array_map('trim', explode('/', $aco)));
311:     }
312: 
313: /**
314:  * build a tree representation from the given allow/deny informations for ACO paths
315:  *
316:  * @param array $allow ACO allow rules
317:  * @param array $deny ACO deny rules
318:  * @return void
319:  */
320:     public function build(array $allow, array $deny = array()) {
321:         $this->_tree = array();
322:         $tree = array();
323: 
324:         foreach ($allow as $dotPath => $aros) {
325:             if (is_string($aros)) {
326:                 $aros = array_map('trim', explode(',', $aros));
327:             }
328: 
329:             $this->access($aros, $dotPath, null, 'allow');
330:         }
331: 
332:         foreach ($deny as $dotPath => $aros) {
333:             if (is_string($aros)) {
334:                 $aros = array_map('trim', explode(',', $aros));
335:             }
336: 
337:             $this->access($aros, $dotPath, null, 'deny');
338:         }
339:     }
340: 
341: }
342: 
343: /**
344:  * Access Request Object
345:  *
346:  */
347: class PhpAro {
348: 
349: /**
350:  * role to resolve to when a provided ARO is not listed in
351:  * the internal tree
352:  *
353:  * @var string
354:  */
355:     const DEFAULT_ROLE = 'Role/default';
356: 
357: /**
358:  * map external identifiers. E.g. if
359:  *
360:  * array('User' => array('username' => 'jeff', 'role' => 'editor'))
361:  *
362:  * is passed as an ARO to one of the methods of AclComponent, PhpAcl
363:  * will check if it can be resolved to an User or a Role defined in the
364:  * configuration file.
365:  *
366:  * @var array
367:  * @see app/Config/acl.php
368:  */
369:     public $map = array(
370:         'User' => 'User/username',
371:         'Role' => 'User/role',
372:     );
373: 
374: /**
375:  * aliases to map
376:  *
377:  * @var array
378:  */
379:     public $aliases = array();
380: 
381: /**
382:  * internal ARO representation
383:  *
384:  * @var array
385:  */
386:     protected $_tree = array();
387: 
388:     public function __construct(array $aro = array(), array $map = array(), array $aliases = array()) {
389:         if (!empty($map)) {
390:             $this->map = $map;
391:         }
392: 
393:         $this->aliases = $aliases;
394:         $this->build($aro);
395:     }
396: 
397: /**
398:  * From the perspective of the given ARO, walk down the tree and
399:  * collect all inherited AROs levelwise such that AROs from different
400:  * branches with equal distance to the requested ARO will be collected at the same
401:  * index. The resulting array will contain a prioritized list of (list of) roles ordered from
402:  * the most distant AROs to the requested one itself.
403:  *
404:  * @param string|array $aro An ARO identifier
405:  * @return array prioritized AROs
406:  */
407:     public function roles($aro) {
408:         $aros = array();
409:         $aro = $this->resolve($aro);
410:         $stack = array(array($aro, 0));
411: 
412:         while (!empty($stack)) {
413:             list($element, $depth) = array_pop($stack);
414:             $aros[$depth][] = $element;
415: 
416:             foreach ($this->_tree as $node => $children) {
417:                 if (in_array($element, $children)) {
418:                     array_push($stack, array($node, $depth + 1));
419:                 }
420:             }
421:         }
422: 
423:         return array_reverse($aros);
424:     }
425: 
426: /**
427:  * resolve an ARO identifier to an internal ARO string using
428:  * the internal mapping information.
429:  *
430:  * @param string|array $aro ARO identifier (User.jeff, array('User' => ...), etc)
431:  * @return string internal aro string (e.g. User/jeff, Role/default)
432:  */
433:     public function resolve($aro) {
434:         foreach ($this->map as $aroGroup => $map) {
435:             list ($model, $field) = explode('/', $map, 2);
436:             $mapped = '';
437: 
438:             if (is_array($aro)) {
439:                 if (isset($aro['model']) && isset($aro['foreign_key']) && $aro['model'] == $aroGroup) {
440:                     $mapped = $aroGroup . '/' . $aro['foreign_key'];
441:                 } elseif (isset($aro[$model][$field])) {
442:                     $mapped = $aroGroup . '/' . $aro[$model][$field];
443:                 } elseif (isset($aro[$field])) {
444:                     $mapped = $aroGroup . '/' . $aro[$field];
445:                 }
446:             } elseif (is_string($aro)) {
447:                 $aro = ltrim($aro, '/');
448: 
449:                 if (strpos($aro, '/') === false) {
450:                     $mapped = $aroGroup . '/' . $aro;
451:                 } else {
452:                     list($aroModel, $aroValue) = explode('/', $aro, 2);
453: 
454:                     $aroModel = Inflector::camelize($aroModel);
455: 
456:                     if ($aroModel == $model || $aroModel == $aroGroup) {
457:                         $mapped = $aroGroup . '/' . $aroValue;
458:                     }
459:                 }
460:             }
461: 
462:             if (isset($this->_tree[$mapped])) {
463:                 return $mapped;
464:             }
465: 
466:             // is there a matching alias defined (e.g. Role/1 => Role/admin)?
467:             if (!empty($this->aliases[$mapped])) {
468:                 return $this->aliases[$mapped];
469:             }
470:         }
471:         return self::DEFAULT_ROLE;
472:     }
473: 
474: /**
475:  * adds a new ARO to the tree
476:  *
477:  * @param array $aro one or more ARO records
478:  * @return void
479:  */
480:     public function addRole(array $aro) {
481:         foreach ($aro as $role => $inheritedRoles) {
482:             if (!isset($this->_tree[$role])) {
483:                 $this->_tree[$role] = array();
484:             }
485: 
486:             if (!empty($inheritedRoles)) {
487:                 if (is_string($inheritedRoles)) {
488:                     $inheritedRoles = array_map('trim', explode(',', $inheritedRoles));
489:                 }
490: 
491:                 foreach ($inheritedRoles as $dependency) {
492:                     // detect cycles
493:                     $roles = $this->roles($dependency);
494: 
495:                     if (in_array($role, Hash::flatten($roles))) {
496:                         $path = '';
497: 
498:                         foreach ($roles as $roleDependencies) {
499:                             $path .= implode('|', (array)$roleDependencies) . ' -> ';
500:                         }
501: 
502:                         trigger_error(__d('cake_dev', 'cycle detected when inheriting %s from %s. Path: %s', $role, $dependency, $path . $role));
503:                         continue;
504:                     }
505: 
506:                     if (!isset($this->_tree[$dependency])) {
507:                         $this->_tree[$dependency] = array();
508:                     }
509: 
510:                     $this->_tree[$dependency][] = $role;
511:                 }
512:             }
513:         }
514:     }
515: 
516: /**
517:  * adds one or more aliases to the internal map. Overwrites existing entries.
518:  *
519:  * @param array $alias alias from => to (e.g. Role/13 -> Role/editor)
520:  * @return void
521:  */
522:     public  function addAlias(array $alias) {
523:         $this->aliases = array_merge($this->aliases, $alias);
524:     }
525: 
526: /**
527:  * build an ARO tree structure for internal processing
528:  *
529:  * @param array $aros array of AROs as key and their inherited AROs as values
530:  * @return void
531:  */
532:     public function build(array $aros) {
533:         $this->_tree = array();
534:         $this->addRole($aros);
535:     }
536: 
537: }
538: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs