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

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.8
      • 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
  • None

Classes

  • DbAcl
  • IniAcl
  • PhpAcl
  • PhpAco
  • PhpAro

Interfaces

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