Packages

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • NIdentity
  • NPermission
  • NSimpleAuthenticator

Interfaces

  • IAuthenticator
  • IAuthorizator
  • IIdentity
  • IPermissionAssertion
  • IResource
  • IRole

Exceptions

  • NAuthenticationException
  • Overview
  • Package
  • Class
  • Tree
  • Other releases
   1: <?php
   2: 
   3: /**
   4:  * This file is part of the Nette Framework (https://nette.org)
   5:  *
   6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
   7:  *
   8:  * For the full copyright and license information, please view
   9:  * the file license.txt that was distributed with this source code.
  10:  * @package Nette\Security
  11:  */
  12: 
  13: 
  14: 
  15: /**
  16:  * Access control list (ACL) functionality and privileges management.
  17:  *
  18:  * This solution is mostly based on Zend_Acl (c) Zend Technologies USA Inc. (http://www.zend.com), new BSD license
  19:  *
  20:  * @copyright  Copyright (c) 2005, 2007 Zend Technologies USA Inc.
  21:  * @author     David Grudl
  22:  * @package Nette\Security
  23:  */
  24: class NPermission extends NObject implements IAuthorizator
  25: {
  26:     /** @var array  Role storage */
  27:     private $roles = array();
  28: 
  29:     /** @var array  Resource storage */
  30:     private $resources = array();
  31: 
  32:     /** @var array  Access Control List rules; whitelist (deny everything to all) by default */
  33:     private $rules = array(
  34:         'allResources' => array(
  35:             'allRoles' => array(
  36:                 'allPrivileges' => array(
  37:                     'type'   => self::DENY,
  38:                     'assert' => NULL,
  39:                 ),
  40:                 'byPrivilege' => array(),
  41:             ),
  42:             'byRole' => array(),
  43:         ),
  44:         'byResource' => array(),
  45:     );
  46: 
  47:     /** @var mixed */
  48:     private $queriedRole, $queriedResource;
  49: 
  50: 
  51: 
  52:     /********************* roles ****************d*g**/
  53: 
  54: 
  55:     /**
  56:      * Adds a Role to the list.
  57:      *
  58:      * The $parents parameter may be a Role identifier (or array of identifiers)
  59:      * to indicate the Roles from which the newly added Role will directly inherit.
  60:      *
  61:      * In order to resolve potential ambiguities with conflicting rules inherited
  62:      * from different parents, the most recently added parent takes precedence over
  63:      * parents that were previously added. In other words, the first parent added
  64:      * will have the least priority, and the last parent added will have the
  65:      * highest priority.
  66:      *
  67:      * @param  string
  68:      * @param  string|array
  69:      * @throws InvalidArgumentException
  70:      * @throws InvalidStateException
  71:      * @return NPermission  provides a fluent interface
  72:      */
  73:     public function addRole($role, $parents = NULL)
  74:     {
  75:         $this->checkRole($role, FALSE);
  76: 
  77:         if (isset($this->roles[$role])) {
  78:             throw new InvalidStateException("Role '$role' already exists in the list.");
  79:         }
  80: 
  81:         $roleParents = array();
  82: 
  83:         if ($parents !== NULL) {
  84:             if (!is_array($parents)) {
  85:                 $parents = array($parents);
  86:             }
  87: 
  88:             foreach ($parents as $parent) {
  89:                 $this->checkRole($parent);
  90:                 $roleParents[$parent] = TRUE;
  91:                 $this->roles[$parent]['children'][$role] = TRUE;
  92:             }
  93:         }
  94: 
  95:         $this->roles[$role] = array(
  96:             'parents'  => $roleParents,
  97:             'children' => array(),
  98:         );
  99: 
 100:         return $this;
 101:     }
 102: 
 103: 
 104: 
 105:     /**
 106:      * Returns TRUE if the Role exists in the list.
 107:      * @param  string
 108:      * @return bool
 109:      */
 110:     public function hasRole($role)
 111:     {
 112:         $this->checkRole($role, FALSE);
 113:         return isset($this->roles[$role]);
 114:     }
 115: 
 116: 
 117: 
 118:     /**
 119:      * Checks whether Role is valid and exists in the list.
 120:      * @param  string
 121:      * @param  bool
 122:      * @throws InvalidStateException
 123:      * @return void
 124:      */
 125:     private function checkRole($role, $need = TRUE)
 126:     {
 127:         if (!is_string($role) || $role === '') {
 128:             throw new InvalidArgumentException("Role must be a non-empty string.");
 129: 
 130:         } elseif ($need && !isset($this->roles[$role])) {
 131:             throw new InvalidStateException("Role '$role' does not exist.");
 132:         }
 133:     }
 134: 
 135: 
 136: 
 137:     /**
 138:      * Returns an array of an existing Role's parents.
 139:      *
 140:      * The parent Roles are ordered in this array by ascending priority.
 141:      * The highest priority parent Role, last in the array, corresponds with
 142:      * the parent Role most recently added.
 143:      *
 144:      * If the Role does not have any parents, then an empty array is returned.
 145:      *
 146:      * @param  string
 147:      * @return array
 148:      */
 149:     public function getRoleParents($role)
 150:     {
 151:         $this->checkRole($role);
 152:         return array_keys($this->roles[$role]['parents']);
 153:     }
 154: 
 155: 
 156: 
 157:     /**
 158:      * Returns TRUE if $role inherits from $inherit.
 159:      *
 160:      * If $onlyParents is TRUE, then $role must inherit directly from
 161:      * $inherit in order to return TRUE. By default, this method looks
 162:      * through the entire inheritance DAG to determine whether $role
 163:      * inherits from $inherit through its ancestor Roles.
 164:      *
 165:      * @param  string
 166:      * @param  string
 167:      * @param  boolean
 168:      * @throws InvalidStateException
 169:      * @return bool
 170:      */
 171:     public function roleInheritsFrom($role, $inherit, $onlyParents = FALSE)
 172:     {
 173:         $this->checkRole($role);
 174:         $this->checkRole($inherit);
 175: 
 176:         $inherits = isset($this->roles[$role]['parents'][$inherit]);
 177: 
 178:         if ($inherits || $onlyParents) {
 179:             return $inherits;
 180:         }
 181: 
 182:         foreach ($this->roles[$role]['parents'] as $parent => $foo) {
 183:             if ($this->roleInheritsFrom($parent, $inherit)) {
 184:                 return TRUE;
 185:             }
 186:         }
 187: 
 188:         return FALSE;
 189:     }
 190: 
 191: 
 192: 
 193:     /**
 194:      * Removes the Role from the list.
 195:      *
 196:      * @param  string
 197:      * @throws InvalidStateException
 198:      * @return NPermission  provides a fluent interface
 199:      */
 200:     public function removeRole($role)
 201:     {
 202:         $this->checkRole($role);
 203: 
 204:         foreach ($this->roles[$role]['children'] as $child => $foo)
 205:             unset($this->roles[$child]['parents'][$role]);
 206: 
 207:         foreach ($this->roles[$role]['parents'] as $parent => $foo)
 208:             unset($this->roles[$parent]['children'][$role]);
 209: 
 210:         unset($this->roles[$role]);
 211: 
 212:         foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules) {
 213:             if ($role === $roleCurrent) {
 214:                 unset($this->rules['allResources']['byRole'][$roleCurrent]);
 215:             }
 216:         }
 217: 
 218:         foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
 219:             if (isset($visitor['byRole'])) {
 220:                 foreach ($visitor['byRole'] as $roleCurrent => $rules) {
 221:                     if ($role === $roleCurrent) {
 222:                         unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
 223:                     }
 224:                 }
 225:             }
 226:         }
 227: 
 228:         return $this;
 229:     }
 230: 
 231: 
 232: 
 233:     /**
 234:      * Removes all Roles from the list.
 235:      *
 236:      * @return NPermission  provides a fluent interface
 237:      */
 238:     public function removeAllRoles()
 239:     {
 240:         $this->roles = array();
 241: 
 242:         foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules)
 243:             unset($this->rules['allResources']['byRole'][$roleCurrent]);
 244: 
 245:         foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
 246:             foreach ($visitor['byRole'] as $roleCurrent => $rules) {
 247:                 unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
 248:             }
 249:         }
 250: 
 251:         return $this;
 252:     }
 253: 
 254: 
 255: 
 256:     /********************* resources ****************d*g**/
 257: 
 258: 
 259: 
 260:     /**
 261:      * Adds a Resource having an identifier unique to the list.
 262:      *
 263:      * @param  string
 264:      * @param  string
 265:      * @throws InvalidArgumentException
 266:      * @throws InvalidStateException
 267:      * @return NPermission  provides a fluent interface
 268:      */
 269:     public function addResource($resource, $parent = NULL)
 270:     {
 271:         $this->checkResource($resource, FALSE);
 272: 
 273:         if (isset($this->resources[$resource])) {
 274:             throw new InvalidStateException("Resource '$resource' already exists in the list.");
 275:         }
 276: 
 277:         if ($parent !== NULL) {
 278:             $this->checkResource($parent);
 279:             $this->resources[$parent]['children'][$resource] = TRUE;
 280:         }
 281: 
 282:         $this->resources[$resource] = array(
 283:             'parent'   => $parent,
 284:             'children' => array()
 285:         );
 286: 
 287:         return $this;
 288:     }
 289: 
 290: 
 291: 
 292:     /**
 293:      * Returns TRUE if the Resource exists in the list.
 294:      * @param  string
 295:      * @return bool
 296:      */
 297:     public function hasResource($resource)
 298:     {
 299:         $this->checkResource($resource, FALSE);
 300:         return isset($this->resources[$resource]);
 301:     }
 302: 
 303: 
 304: 
 305:     /**
 306:      * Checks whether Resource is valid and exists in the list.
 307:      * @param  string
 308:      * @param  bool
 309:      * @throws InvalidStateException
 310:      * @return void
 311:      */
 312:     private function checkResource($resource, $need = TRUE)
 313:     {
 314:         if (!is_string($resource) || $resource === '') {
 315:             throw new InvalidArgumentException("Resource must be a non-empty string.");
 316: 
 317:         } elseif ($need && !isset($this->resources[$resource])) {
 318:             throw new InvalidStateException("Resource '$resource' does not exist.");
 319:         }
 320:     }
 321: 
 322: 
 323: 
 324:     /**
 325:      * Returns TRUE if $resource inherits from $inherit.
 326:      *
 327:      * If $onlyParents is TRUE, then $resource must inherit directly from
 328:      * $inherit in order to return TRUE. By default, this method looks
 329:      * through the entire inheritance tree to determine whether $resource
 330:      * inherits from $inherit through its ancestor Resources.
 331:      *
 332:      * @param  string
 333:      * @param  string
 334:      * @param  boolean
 335:      * @throws InvalidStateException
 336:      * @return bool
 337:      */
 338:     public function resourceInheritsFrom($resource, $inherit, $onlyParent = FALSE)
 339:     {
 340:         $this->checkResource($resource);
 341:         $this->checkResource($inherit);
 342: 
 343:         if ($this->resources[$resource]['parent'] === NULL) {
 344:             return FALSE;
 345:         }
 346: 
 347:         $parent = $this->resources[$resource]['parent'];
 348:         if ($inherit === $parent) {
 349:             return TRUE;
 350: 
 351:         } elseif ($onlyParent) {
 352:             return FALSE;
 353:         }
 354: 
 355:         while ($this->resources[$parent]['parent'] !== NULL) {
 356:             $parent = $this->resources[$parent]['parent'];
 357:             if ($inherit === $parent) {
 358:                 return TRUE;
 359:             }
 360:         }
 361: 
 362:         return FALSE;
 363:     }
 364: 
 365: 
 366: 
 367:     /**
 368:      * Removes a Resource and all of its children.
 369:      *
 370:      * @param  string
 371:      * @throws InvalidStateException
 372:      * @return NPermission  provides a fluent interface
 373:      */
 374:     public function removeResource($resource)
 375:     {
 376:         $this->checkResource($resource);
 377: 
 378:         $parent = $this->resources[$resource]['parent'];
 379:         if ($parent !== NULL) {
 380:             unset($this->resources[$parent]['children'][$resource]);
 381:         }
 382: 
 383:         $removed = array($resource);
 384:         foreach ($this->resources[$resource]['children'] as $child => $foo) {
 385:             $this->removeResource($child);
 386:             $removed[] = $child;
 387:         }
 388: 
 389:         foreach ($removed as $resourceRemoved) {
 390:             foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
 391:                 if ($resourceRemoved === $resourceCurrent) {
 392:                     unset($this->rules['byResource'][$resourceCurrent]);
 393:                 }
 394:             }
 395:         }
 396: 
 397:         unset($this->resources[$resource]);
 398: 
 399:         return $this;
 400:     }
 401: 
 402: 
 403: 
 404:     /**
 405:      * Removes all Resources.
 406:      *
 407:      * @return NPermission  provides a fluent interface
 408:      */
 409:     public function removeAllResources()
 410:     {
 411:         foreach ($this->resources as $resource => $foo) {
 412:             foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
 413:                 if ($resource === $resourceCurrent) {
 414:                     unset($this->rules['byResource'][$resourceCurrent]);
 415:                 }
 416:             }
 417:         }
 418: 
 419:         $this->resources = array();
 420:         return $this;
 421:     }
 422: 
 423: 
 424: 
 425:     /********************* defining rules ****************d*g**/
 426: 
 427: 
 428: 
 429:     /**
 430:      * Adds an "allow" rule to the list. A rule is added that would allow one
 431:      * or more Roles access to [certain $privileges upon] the specified Resource(s).
 432:      *
 433:      * If either $roles or $resources is NPermission::ALL, then the rule applies to all Roles or all Resources,
 434:      * respectively. Both may be NPermission::ALL in order to work with the default rule of the ACL.
 435:      *
 436:      * The $privileges parameter may be used to further specify that the rule applies only
 437:      * to certain privileges upon the Resource(s) in question. This may be specified to be a single
 438:      * privilege with a string, and multiple privileges may be specified as an array of strings.
 439:      *
 440:      * If $assertion is provided, then its assert() method must return TRUE in order for
 441:      * the rule to apply. If $assertion is provided with $roles, $resources, and $privileges all
 442:      * equal to NULL, then a rule will imply a type of DENY when the rule's assertion fails.
 443:      *
 444:      * @param  string|array|NPermission::ALL  roles
 445:      * @param  string|array|NPermission::ALL  resources
 446:      * @param  string|array|NPermission::ALL  privileges
 447:      * @param  IPermissionAssertion  assertion
 448:      * @return NPermission  provides a fluent interface
 449:      */
 450:     public function allow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, IPermissionAssertion $assertion = NULL)
 451:     {
 452:         $this->setRule(TRUE, self::ALLOW, $roles, $resources, $privileges, $assertion);
 453:         return $this;
 454:     }
 455: 
 456: 
 457: 
 458:     /**
 459:      * Adds a "deny" rule to the list. A rule is added that would deny one
 460:      * or more Roles access to [certain $privileges upon] the specified Resource(s).
 461:      *
 462:      * If either $roles or $resources is NPermission::ALL, then the rule applies to all Roles or all Resources,
 463:      * respectively. Both may be NPermission::ALL in order to work with the default rule of the ACL.
 464:      *
 465:      * The $privileges parameter may be used to further specify that the rule applies only
 466:      * to certain privileges upon the Resource(s) in question. This may be specified to be a single
 467:      * privilege with a string, and multiple privileges may be specified as an array of strings.
 468:      *
 469:      * If $assertion is provided, then its assert() method must return TRUE in order for
 470:      * the rule to apply. If $assertion is provided with $roles, $resources, and $privileges all
 471:      * equal to NULL, then a rule will imply a type of ALLOW when the rule's assertion fails.
 472:      *
 473:      * @param  string|array|NPermission::ALL  roles
 474:      * @param  string|array|NPermission::ALL  resources
 475:      * @param  string|array|NPermission::ALL  privileges
 476:      * @param  IPermissionAssertion  assertion
 477:      * @return NPermission  provides a fluent interface
 478:      */
 479:     public function deny($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, IPermissionAssertion $assertion = NULL)
 480:     {
 481:         $this->setRule(TRUE, self::DENY, $roles, $resources, $privileges, $assertion);
 482:         return $this;
 483:     }
 484: 
 485: 
 486: 
 487:     /**
 488:      * Removes "allow" permissions from the list. The rule is removed only in the context
 489:      * of the given Roles, Resources, and privileges. Existing rules to which the remove
 490:      * operation does not apply would remain in the
 491:      *
 492:      * @param  string|array|NPermission::ALL  roles
 493:      * @param  string|array|NPermission::ALL  resources
 494:      * @param  string|array|NPermission::ALL  privileges
 495:      * @return NPermission  provides a fluent interface
 496:      */
 497:     public function removeAllow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL)
 498:     {
 499:         $this->setRule(FALSE, self::ALLOW, $roles, $resources, $privileges);
 500:         return $this;
 501:     }
 502: 
 503: 
 504: 
 505:     /**
 506:      * Removes "deny" restrictions from the list. The rule is removed only in the context
 507:      * of the given Roles, Resources, and privileges. Existing rules to which the remove
 508:      * operation does not apply would remain in the
 509:      *
 510:      * @param  string|array|NPermission::ALL  roles
 511:      * @param  string|array|NPermission::ALL  resources
 512:      * @param  string|array|NPermission::ALL  privileges
 513:      * @return NPermission  provides a fluent interface
 514:      */
 515:     public function removeDeny($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL)
 516:     {
 517:         $this->setRule(FALSE, self::DENY, $roles, $resources, $privileges);
 518:         return $this;
 519:     }
 520: 
 521: 
 522: 
 523:     /**
 524:      * Performs operations on Access Control List rules.
 525:      *
 526:      * @param  bool  operation add?
 527:      * @param  bool  type
 528:      * @param  string|array|NPermission::ALL  roles
 529:      * @param  string|array|NPermission::ALL  resources
 530:      * @param  string|array|NPermission::ALL  privileges
 531:      * @param  IPermissionAssertion assertion
 532:      * @throws InvalidStateException
 533:      * @return NPermission  provides a fluent interface
 534:      */
 535:     protected function setRule($toAdd, $type, $roles, $resources, $privileges, IPermissionAssertion $assertion = NULL)
 536:     {
 537:         // ensure that all specified Roles exist; normalize input to array of Roles or NULL
 538:         if ($roles === self::ALL) {
 539:             $roles = array(self::ALL);
 540: 
 541:         } else {
 542:             if (!is_array($roles)) {
 543:                 $roles = array($roles);
 544:             }
 545: 
 546:             foreach ($roles as $role) {
 547:                 $this->checkRole($role);
 548:             }
 549:         }
 550: 
 551:         // ensure that all specified Resources exist; normalize input to array of Resources or NULL
 552:         if ($resources === self::ALL) {
 553:             $resources = array(self::ALL);
 554: 
 555:         } else {
 556:             if (!is_array($resources)) {
 557:                 $resources = array($resources);
 558:             }
 559: 
 560:             foreach ($resources as $resource) {
 561:                 $this->checkResource($resource);
 562:             }
 563:         }
 564: 
 565:         // normalize privileges to array
 566:         if ($privileges === self::ALL) {
 567:             $privileges = array();
 568: 
 569:         } elseif (!is_array($privileges)) {
 570:             $privileges = array($privileges);
 571:         }
 572: 
 573: 
 574:         if ($toAdd) { // add to the rules
 575:             foreach ($resources as $resource) {
 576:                 foreach ($roles as $role) {
 577:                     $rules = & $this->getRules($resource, $role, TRUE);
 578:                     if (count($privileges) === 0) {
 579:                         $rules['allPrivileges']['type'] = $type;
 580:                         $rules['allPrivileges']['assert'] = $assertion;
 581:                         if (!isset($rules['byPrivilege'])) {
 582:                             $rules['byPrivilege'] = array();
 583:                         }
 584:                     } else {
 585:                         foreach ($privileges as $privilege) {
 586:                             $rules['byPrivilege'][$privilege]['type'] = $type;
 587:                             $rules['byPrivilege'][$privilege]['assert'] = $assertion;
 588:                         }
 589:                     }
 590:                 }
 591:             }
 592: 
 593:         } else { // remove from the rules
 594:             foreach ($resources as $resource) {
 595:                 foreach ($roles as $role) {
 596:                     $rules = & $this->getRules($resource, $role);
 597:                     if ($rules === NULL) {
 598:                         continue;
 599:                     }
 600:                     if (count($privileges) === 0) {
 601:                         if ($resource === self::ALL && $role === self::ALL) {
 602:                             if ($type === $rules['allPrivileges']['type']) {
 603:                                 $rules = array(
 604:                                     'allPrivileges' => array(
 605:                                         'type'   => self::DENY,
 606:                                         'assert' => NULL
 607:                                         ),
 608:                                     'byPrivilege' => array()
 609:                                     );
 610:                             }
 611:                             continue;
 612:                         }
 613:                         if ($type === $rules['allPrivileges']['type']) {
 614:                             unset($rules['allPrivileges']);
 615:                         }
 616:                     } else {
 617:                         foreach ($privileges as $privilege) {
 618:                             if (isset($rules['byPrivilege'][$privilege]) &&
 619:                                 $type === $rules['byPrivilege'][$privilege]['type']) {
 620:                                 unset($rules['byPrivilege'][$privilege]);
 621:                             }
 622:                         }
 623:                     }
 624:                 }
 625:             }
 626:         }
 627:         return $this;
 628:     }
 629: 
 630: 
 631: 
 632:     /********************* querying the ACL ****************d*g**/
 633: 
 634: 
 635: 
 636:     /**
 637:      * Returns TRUE if and only if the Role has access to the Resource.
 638:      *
 639:      * If either $role or $resource is NPermission::ALL, then the query applies to all Roles or all Resources,
 640:      * respectively. Both may be NPermission::ALL to query whether the ACL has a "blacklist" rule
 641:      * (allow everything to all). By default, Permission creates a "whitelist" rule (deny
 642:      * everything to all), and this method would return FALSE unless this default has
 643:      * been overridden (i.e., by executing $acl->allow()).
 644:      *
 645:      * If a $privilege is not provided, then this method returns FALSE if and only if the
 646:      * Role is denied access to at least one privilege upon the Resource. In other words, this
 647:      * method returns TRUE if and only if the Role is allowed all privileges on the Resource.
 648:      *
 649:      * This method checks Role inheritance using a depth-first traversal of the Role list.
 650:      * The highest priority parent (i.e., the parent most recently added) is checked first,
 651:      * and its respective parents are checked similarly before the lower-priority parents of
 652:      * the Role are checked.
 653:      *
 654:      * @param  string|NPermission::ALL|IRole  role
 655:      * @param  string|NPermission::ALL|IResource  resource
 656:      * @param  string|NPermission::ALL  privilege
 657:      * @throws InvalidStateException
 658:      * @return bool
 659:      */
 660:     public function isAllowed($role = self::ALL, $resource = self::ALL, $privilege = self::ALL)
 661:     {
 662:         $this->queriedRole = $role;
 663:         if ($role !== self::ALL) {
 664:             if ($role instanceof IRole) {
 665:                 $role = $role->getRoleId();
 666:             }
 667:             $this->checkRole($role);
 668:         }
 669: 
 670:         $this->queriedResource = $resource;
 671:         if ($resource !== self::ALL) {
 672:             if ($resource instanceof IResource) {
 673:                 $resource = $resource->getResourceId();
 674:             }
 675:             $this->checkResource($resource);
 676:         }
 677: 
 678:         if ($privilege === self::ALL) {
 679:             // query on all privileges
 680:             do {
 681:                 // depth-first search on $role if it is not 'allRoles' pseudo-parent
 682:                 if ($role !== NULL && NULL !== ($result = $this->roleDFSAllPrivileges($role, $resource))) {
 683:                     break;
 684:                 }
 685: 
 686:                 // look for rule on 'allRoles' psuedo-parent
 687:                 if (NULL !== ($rules = $this->getRules($resource, self::ALL))) {
 688:                     foreach ($rules['byPrivilege'] as $privilege => $rule) {
 689:                         if (self::DENY === ($ruleTypeOnePrivilege = $this->getRuleType($resource, NULL, $privilege))) {
 690:                             $result = self::DENY;
 691:                             break 2;
 692:                         }
 693:                     }
 694:                     if (NULL !== ($ruleTypeAllPrivileges = $this->getRuleType($resource, NULL, NULL))) {
 695:                         $result = self::ALLOW === $ruleTypeAllPrivileges;
 696:                         break;
 697:                     }
 698:                 }
 699: 
 700:                 // try next Resource
 701:                 $resource = $this->resources[$resource]['parent'];
 702: 
 703:             } while (TRUE); // loop terminates at 'allResources' pseudo-parent
 704: 
 705:         } else {
 706:             // query on one privilege
 707:             do {
 708:                 // depth-first search on $role if it is not 'allRoles' pseudo-parent
 709:                 if ($role !== NULL && NULL !== ($result = $this->roleDFSOnePrivilege($role, $resource, $privilege))) {
 710:                     break;
 711:                 }
 712: 
 713:                 // look for rule on 'allRoles' pseudo-parent
 714:                 if (NULL !== ($ruleType = $this->getRuleType($resource, NULL, $privilege))) {
 715:                     $result = self::ALLOW === $ruleType;
 716:                     break;
 717: 
 718:                 } elseif (NULL !== ($ruleTypeAllPrivileges = $this->getRuleType($resource, NULL, NULL))) {
 719:                     $result = self::ALLOW === $ruleTypeAllPrivileges;
 720:                     break;
 721:                 }
 722: 
 723:                 // try next Resource
 724:                 $resource = $this->resources[$resource]['parent'];
 725: 
 726:             } while (TRUE); // loop terminates at 'allResources' pseudo-parent
 727:         }
 728: 
 729:         $this->queriedRole = $this->queriedResource = NULL;
 730:         return $result;
 731:     }
 732: 
 733: 
 734: 
 735:     /**
 736:      * Returns real currently queried Role. Use by {@link IPermissionAssertion::asert()}.
 737:      * @return mixed
 738:      */
 739:     public function getQueriedRole()
 740:     {
 741:         return $this->queriedRole;
 742:     }
 743: 
 744: 
 745: 
 746:     /**
 747:      * Returns real currently queried Resource. Use by {@link IPermissionAssertion::asert()}.
 748:      * @return mixed
 749:      */
 750:     public function getQueriedResource()
 751:     {
 752:         return $this->queriedResource;
 753:     }
 754: 
 755: 
 756: 
 757:     /********************* internals ****************d*g**/
 758: 
 759: 
 760: 
 761:     /**
 762:      * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule.
 763:      * allowing/denying $role access to all privileges upon $resource
 764:      *
 765:      * This method returns TRUE if a rule is found and allows access. If a rule exists and denies access,
 766:      * then this method returns FALSE. If no applicable rule is found, then this method returns NULL.
 767:      *
 768:      * @param  string  role
 769:      * @param  string  resource
 770:      * @return bool|NULL
 771:      */
 772:     private function roleDFSAllPrivileges($role, $resource)
 773:     {
 774:         $dfs = array(
 775:             'visited' => array(),
 776:             'stack'   => array($role),
 777:         );
 778: 
 779:         while (NULL !== ($role = array_pop($dfs['stack']))) {
 780:             if (!isset($dfs['visited'][$role])) {
 781:                 if (NULL !== ($result = $this->roleDFSVisitAllPrivileges($role, $resource, $dfs))) {
 782:                     return $result;
 783:                 }
 784:             }
 785:         }
 786: 
 787:         return NULL;
 788:     }
 789: 
 790: 
 791: 
 792:     /**
 793:      * Visits a $role in order to look for a rule allowing/denying $role access to all privileges upon $resource.
 794:      *
 795:      * This method returns TRUE if a rule is found and allows access. If a rule exists and denies access,
 796:      * then this method returns FALSE. If no applicable rule is found, then this method returns NULL.
 797:      *
 798:      * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
 799:      *
 800:      * @param  string  role
 801:      * @param  string  resource
 802:      * @param  array   dfs
 803:      * @return bool|NULL
 804:      */
 805:     private function roleDFSVisitAllPrivileges($role, $resource, &$dfs)
 806:     {
 807:         if (NULL !== ($rules = $this->getRules($resource, $role))) {
 808:             foreach ($rules['byPrivilege'] as $privilege => $rule) {
 809:                 if (self::DENY === $this->getRuleType($resource, $role, $privilege)) {
 810:                     return self::DENY;
 811:                 }
 812:             }
 813:             if (NULL !== ($type = $this->getRuleType($resource, $role, NULL))) {
 814:                 return self::ALLOW === $type;
 815:             }
 816:         }
 817: 
 818:         $dfs['visited'][$role] = TRUE;
 819:         foreach ($this->roles[$role]['parents'] as $roleParent => $foo) {
 820:             $dfs['stack'][] = $roleParent;
 821:         }
 822: 
 823:         return NULL;
 824:     }
 825: 
 826: 
 827: 
 828:     /**
 829:      * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule.
 830:      * allowing/denying $role access to a $privilege upon $resource
 831:      *
 832:      * This method returns TRUE if a rule is found and allows access. If a rule exists and denies access,
 833:      * then this method returns FALSE. If no applicable rule is found, then this method returns NULL.
 834:      *
 835:      * @param  string  role
 836:      * @param  string  resource
 837:      * @param  string  privilege
 838:      * @return bool|NULL
 839:      */
 840:     private function roleDFSOnePrivilege($role, $resource, $privilege)
 841:     {
 842:         $dfs = array(
 843:             'visited' => array(),
 844:             'stack'   => array($role),
 845:         );
 846: 
 847:         while (NULL !== ($role = array_pop($dfs['stack']))) {
 848:             if (!isset($dfs['visited'][$role])) {
 849:                 if (NULL !== ($result = $this->roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) {
 850:                     return $result;
 851:                 }
 852:             }
 853:         }
 854: 
 855:         return NULL;
 856:     }
 857: 
 858: 
 859: 
 860:     /**
 861:      * Visits a $role in order to look for a rule allowing/denying $role access to a $privilege upon $resource.
 862:      *
 863:      * This method returns TRUE if a rule is found and allows access. If a rule exists and denies access,
 864:      * then this method returns FALSE. If no applicable rule is found, then this method returns NULL.
 865:      *
 866:      * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
 867:      *
 868:      * @param  string  role
 869:      * @param  string  resource
 870:      * @param  string  privilege
 871:      * @param  array   dfs
 872:      * @return bool|NULL
 873:      */
 874:     private function roleDFSVisitOnePrivilege($role, $resource, $privilege, &$dfs)
 875:     {
 876:         if (NULL !== ($type = $this->getRuleType($resource, $role, $privilege))) {
 877:             return self::ALLOW === $type;
 878:         }
 879: 
 880:         if (NULL !== ($type = $this->getRuleType($resource, $role, NULL))) {
 881:             return self::ALLOW === $type;
 882:         }
 883: 
 884:         $dfs['visited'][$role] = TRUE;
 885:         foreach ($this->roles[$role]['parents'] as $roleParent => $foo)
 886:             $dfs['stack'][] = $roleParent;
 887: 
 888:         return NULL;
 889:     }
 890: 
 891: 
 892: 
 893:     /**
 894:      * Returns the rule type associated with the specified Resource, Role, and privilege.
 895:      * combination.
 896:      *
 897:      * If a rule does not exist or its attached assertion fails, which means that
 898:      * the rule is not applicable, then this method returns NULL. Otherwise, the
 899:      * rule type applies and is returned as either ALLOW or DENY.
 900:      *
 901:      * If $resource or $role is NPermission::ALL, then this means that the rule must apply to
 902:      * all Resources or Roles, respectively.
 903:      *
 904:      * If $privilege is NPermission::ALL, then the rule must apply to all privileges.
 905:      *
 906:      * If all three parameters are NPermission::ALL, then the default ACL rule type is returned,
 907:      * based on whether its assertion method passes.
 908:      *
 909:      * @param  string|NPermission::ALL  role
 910:      * @param  string|NPermission::ALL  resource
 911:      * @param  string|NPermission::ALL  privilege
 912:      * @return bool|NULL
 913:      */
 914:     private function getRuleType($resource, $role, $privilege)
 915:     {
 916:         // get the rules for the $resource and $role
 917:         if (NULL === ($rules = $this->getRules($resource, $role))) {
 918:             return NULL;
 919:         }
 920: 
 921:         // follow $privilege
 922:         if ($privilege === self::ALL) {
 923:             if (isset($rules['allPrivileges'])) {
 924:                 $rule = $rules['allPrivileges'];
 925:             } else {
 926:                 return NULL;
 927:             }
 928:         } elseif (!isset($rules['byPrivilege'][$privilege])) {
 929:             return NULL;
 930: 
 931:         } else {
 932:             $rule = $rules['byPrivilege'][$privilege];
 933:         }
 934: 
 935:         // check assertion if necessary
 936:         if ($rule['assert'] === NULL || $rule['assert']->assert($this, $role, $resource, $privilege)) {
 937:             return $rule['type'];
 938: 
 939:         } elseif ($resource !== self::ALL || $role !== self::ALL || $privilege !== self::ALL) {
 940:             return NULL;
 941: 
 942:         } elseif (self::ALLOW === $rule['type']) {
 943:             return self::DENY;
 944: 
 945:         } else {
 946:             return self::ALLOW;
 947:         }
 948:     }
 949: 
 950: 
 951: 
 952:     /**
 953:      * Returns the rules associated with a Resource and a Role, or NULL if no such rules exist.
 954:      *
 955:      * If either $resource or $role is NPermission::ALL, this means that the rules returned are for all Resources or all Roles,
 956:      * respectively. Both can be NPermission::ALL to return the default rule set for all Resources and all Roles.
 957:      *
 958:      * If the $create parameter is TRUE, then a rule set is first created and then returned to the caller.
 959:      *
 960:      * @param  string|NPermission::ALL  resource
 961:      * @param  string|NPermission::ALL  role
 962:      * @param  boolean  create
 963:      * @return array|NULL
 964:      */
 965:     private function & getRules($resource, $role, $create = FALSE)
 966:     {
 967:         // follow $resource
 968:         if ($resource === self::ALL) {
 969:             $visitor = & $this->rules['allResources'];
 970:         } else {
 971:             if (!isset($this->rules['byResource'][$resource])) {
 972:                 if (!$create) {
 973:                     $null = NULL;
 974:                     return $null;
 975:                 }
 976:                 $this->rules['byResource'][$resource] = array();
 977:             }
 978:             $visitor = & $this->rules['byResource'][$resource];
 979:         }
 980: 
 981: 
 982:         // follow $role
 983:         if ($role === self::ALL) {
 984:             if (!isset($visitor['allRoles'])) {
 985:                 if (!$create) {
 986:                     $null = NULL;
 987:                     return $null;
 988:                 }
 989:                 $visitor['allRoles']['byPrivilege'] = array();
 990:             }
 991:             return $visitor['allRoles'];
 992:         }
 993: 
 994:         if (!isset($visitor['byRole'][$role])) {
 995:             if (!$create) {
 996:                 $null = NULL;
 997:                 return $null;
 998:             }
 999:             $visitor['byRole'][$role]['byPrivilege'] = array();
1000:         }
1001: 
1002:         return $visitor['byRole'][$role];
1003:     }
1004: 
1005: }
1006: 
Nette Framework 0.9.7 (for PHP 5.2) API documentation generated by ApiGen 2.3.0