diff --git a/docs/04. Guards.md b/docs/04. Guards.md index 85a4d3a..6819561 100644 --- a/docs/04. Guards.md +++ b/docs/04. Guards.md @@ -177,17 +177,34 @@ return [ 'guards' => [ 'ZfcRbac\Guard\RoutePermissionsGuard' => [ 'admin*' => ['admin'], - 'post/manage' => ['post.update', 'post.delete'] + 'post/manage' => ['post.update', 'post.delete'] ] ] ] ]; ``` -> All permissions in a rule must be matched (it is an AND condition). +> By default, all permissions in a rule must be matched (an AND condition). In the previous example, one must have ```post.update``` **AND** ```post.delete``` permissions -to access the ```post/manage``` route. +to access the ```post/manage``` route. You can also specify an OR condition like so: + +```php +use ZfcRbac\Guard\GuardInterface; + +return [ + 'zfc_rbac' => [ + 'guards' => [ + 'ZfcRbac\Guard\RoutePermissionsGuard' => [ + 'post/manage' => [ + 'permissions' => ['post.update', 'post.delete'], + 'condition' => GuardInterface::CONDITION_OR + ] + ] + ] + ] +]; +``` > Permissions are linked to roles, not to users diff --git a/src/ZfcRbac/Guard/GuardInterface.php b/src/ZfcRbac/Guard/GuardInterface.php index 05f1953..4a88f06 100644 --- a/src/ZfcRbac/Guard/GuardInterface.php +++ b/src/ZfcRbac/Guard/GuardInterface.php @@ -47,6 +47,12 @@ interface GuardInterface extends ListenerAggregateInterface const POLICY_DENY = 'deny'; const POLICY_ALLOW = 'allow'; + /** + * Condition constants + */ + const CONDITION_OR = 'OR'; + const CONDITION_AND = 'AND'; + /** * @param MvcEvent $event * @return bool diff --git a/src/ZfcRbac/Guard/RoutePermissionsGuard.php b/src/ZfcRbac/Guard/RoutePermissionsGuard.php index 577a9c8..f7f72d2 100644 --- a/src/ZfcRbac/Guard/RoutePermissionsGuard.php +++ b/src/ZfcRbac/Guard/RoutePermissionsGuard.php @@ -18,7 +18,6 @@ namespace ZfcRbac\Guard; use Zend\Mvc\MvcEvent; -use ZfcRbac\Exception; use ZfcRbac\Service\AuthorizationServiceInterface; /** @@ -101,12 +100,37 @@ public function isGranted(MvcEvent $event) return true; } - foreach ($allowedPermissions as $permission) { - if (!$this->authorizationService->isGranted($permission)) { - return false; + $permissions = isset($allowedPermissions['permissions']) + ? $allowedPermissions['permissions'] + : $allowedPermissions; + + $condition = isset($allowedPermissions['condition']) + ? $allowedPermissions['condition'] + : GuardInterface::CONDITION_AND; + + if (GuardInterface::CONDITION_AND === $condition) { + foreach ($permissions as $permission) { + if (!$this->authorizationService->isGranted($permission)) { + return false; + } + } + + return true; + } + + if (GuardInterface::CONDITION_OR === $condition) { + foreach ($permissions as $permission) { + if ($this->authorizationService->isGranted($permission)) { + return true; + } } + + return false; } - return true; + throw new InvalidArgumentException(sprintf( + 'Condition must be either "AND" or "OR", %s given', + is_object($condition) ? get_class($condition) : gettype($condition) + )); } } diff --git a/tests/ZfcRbacTest/Guard/RoutePermissionsGuardTest.php b/tests/ZfcRbacTest/Guard/RoutePermissionsGuardTest.php index 0de7740..4420546 100644 --- a/tests/ZfcRbacTest/Guard/RoutePermissionsGuardTest.php +++ b/tests/ZfcRbacTest/Guard/RoutePermissionsGuardTest.php @@ -326,6 +326,26 @@ public function routeDataProvider() 'isGranted' => true, 'policy' => GuardInterface::POLICY_DENY ], + [ + 'rules' => ['route' => [ + 'permissions' => ['post.edit', 'post.read'], + 'condition' => GuardInterface::CONDITION_OR + ]], + 'matchedRouteName' => 'route', + 'identityPermissions' => [['post.edit', null, true]], + 'isGranted' => true, + 'policy' => GuardInterface::POLICY_DENY + ], + [ + 'rules' => ['route' => [ + 'permissions' => ['post.edit', 'post.read'], + 'condition' => GuardInterface::CONDITION_AND + ]], + 'matchedRouteName' => 'route', + 'identityPermissions' => [['post.edit', null, true]], + 'isGranted' => false, + 'policy' => GuardInterface::POLICY_DENY + ] ]; }