Namespaces

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

Classes

  • Identity
  • Permission
  • SimpleAuthenticator

Interfaces

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

Exceptions

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