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.3 API

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