diff --git a/src/ZfcRbac/Guard/ControllerPermissionsGuard.php b/src/ZfcRbac/Guard/ControllerPermissionsGuard.php index bdf85f9..bfd0098 100644 --- a/src/ZfcRbac/Guard/ControllerPermissionsGuard.php +++ b/src/ZfcRbac/Guard/ControllerPermissionsGuard.php @@ -68,8 +68,9 @@ public function __construct(AuthorizationServiceInterface $authorizationService, * * [ * 'controller' => 'ControllerName', - * 'actions' => []/string - * 'roles' => []/string + * 'actions' => []/string, + * 'roles' => []/string, + * 'condition' => GuardInterface::CONDITION_AND/GuardInterface::CONDITION_OR * ] * * @param array $rules @@ -84,6 +85,9 @@ public function setRules(array $rules) $actions = isset($rule['actions']) ? (array)$rule['actions'] : []; $permissions = (array)$rule['permissions']; + // Set condition AND is default + $this->rules[$controller][1] = isset($rule['condition'])? $rule['condition'] :GuardInterface::CONDITION_AND; + if (empty($actions)) { $this->rules[$controller][0] = $permissions; continue; @@ -130,12 +134,39 @@ public function isGranted(MvcEvent $event) return true; } - foreach ($allowedPermissions as $permission) { - if (!$this->authorizationService->isGranted($permission)) { - return false; + /** + * GuardInterface::CONDITION_AND|GuardInterface::CONDITION_OR + * Default condition is 'AND' and it is set in 'setRules' method + * + * @var string Guard permissions condition + */ + $condition = $this->rules[$controller][1]; + + if (GuardInterface::CONDITION_AND === $condition) { + foreach ($allowedPermissions as $permission) { + if (!$this->authorizationService->isGranted($permission)) { + return false; + } + } + + return true; + } + + if (GuardInterface::CONDITION_OR === $condition) { + foreach ($allowedPermissions as $permission) { + if ($this->authorizationService->isGranted($permission)) { + return true; + } } + + return false; } + throw new InvalidArgumentException(sprintf( + 'Condition must be either "AND" or "OR", %s given', + is_object($condition) ? get_class($condition) : gettype($condition) + )); + return true; } } diff --git a/tests/ZfcRbacTest/Guard/ControllerPermissionsGuardTest.php b/tests/ZfcRbacTest/Guard/ControllerPermissionsGuardTest.php index 99377e7..7743a4d 100644 --- a/tests/ZfcRbacTest/Guard/ControllerPermissionsGuardTest.php +++ b/tests/ZfcRbacTest/Guard/ControllerPermissionsGuardTest.php @@ -73,9 +73,34 @@ public function rulesConversionProvider() ]) ], 'expected' => [ - 'mycontroller' => [0 => ['post.manage']], - 'mycontroller2' => [0 => ['post.update', 'post.delete']], - 'mycontroller3' => [0 => ['post.manage']] + 'mycontroller' => [0 => ['post.manage'], 1 => GuardInterface::CONDITION_AND,], + 'mycontroller2' => [0 => ['post.update', 'post.delete'], 1 => GuardInterface::CONDITION_AND,], + 'mycontroller3' => [0 => ['post.manage'], 1 => GuardInterface::CONDITION_AND,] + ] + ], + // Without actions condition or + [ + 'rules' => [ + [ + 'controller' => 'MyController', + 'permissions' => 'post.manage', + 'condition' => GuardInterface::CONDITION_OR, + ], + [ + 'controller' => 'MyController2', + 'permissions' => ['post.update', 'post.delete'], + 'condition' => GuardInterface::CONDITION_OR, + ], + new \ArrayIterator([ + 'controller' => 'MyController3', + 'permissions' => new \ArrayIterator(['post.manage']), + 'condition' => GuardInterface::CONDITION_OR, + ]) + ], + 'expected' => [ + 'mycontroller' => [0 => ['post.manage'], 1 => GuardInterface::CONDITION_OR,], + 'mycontroller2' => [0 => ['post.update', 'post.delete'], 1 => GuardInterface::CONDITION_OR,], + 'mycontroller3' => [0 => ['post.manage'], 1 => GuardInterface::CONDITION_OR,] ] ], // With one action @@ -99,13 +124,53 @@ public function rulesConversionProvider() ], 'expected' => [ 'mycontroller' => [ - 'delete' => ['permission1'] + 'delete' => ['permission1'], + 1 => GuardInterface::CONDITION_AND, + ], + 'mycontroller2' => [ + 'delete' => ['permission2'], + 1 => GuardInterface::CONDITION_AND, + ], + 'mycontroller3' => [ + 'delete' => ['permission3'], + 1 => GuardInterface::CONDITION_AND, + ], + ] + ], + // With one action and condition or + [ + 'rules' => [ + [ + 'controller' => 'MyController', + 'actions' => 'DELETE', + 'permissions' => 'permission1', + 'condition' => GuardInterface::CONDITION_OR, + ], + [ + 'controller' => 'MyController2', + 'actions' => ['delete'], + 'permissions' => 'permission2', + 'condition' => GuardInterface::CONDITION_OR, + ], + new \ArrayIterator([ + 'controller' => 'MyController3', + 'actions' => new \ArrayIterator(['DELETE']), + 'permissions' => new \ArrayIterator(['permission3']), + 'condition' => GuardInterface::CONDITION_OR, + ]) + ], + 'expected' => [ + 'mycontroller' => [ + 'delete' => ['permission1'], + 1 => GuardInterface::CONDITION_OR, ], 'mycontroller2' => [ - 'delete' => ['permission2'] + 'delete' => ['permission2'], + 1 => GuardInterface::CONDITION_OR, ], 'mycontroller3' => [ - 'delete' => ['permission3'] + 'delete' => ['permission3'], + 1 => GuardInterface::CONDITION_OR, ], ] ], @@ -126,11 +191,42 @@ public function rulesConversionProvider() 'expected' => [ 'mycontroller' => [ 'edit' => ['permission1'], - 'delete' => ['permission1'] + 'delete' => ['permission1'], + 1 => GuardInterface::CONDITION_AND, ], 'mycontroller2' => [ 'edit' => ['permission2'], - 'delete' => ['permission2'] + 'delete' => ['permission2'], + 1 => GuardInterface::CONDITION_AND, + ] + ] + ], + // With multiple actions condition or + [ + 'rules' => [ + [ + 'controller' => 'MyController', + 'actions' => ['EDIT', 'delete'], + 'permissions' => 'permission1', + 'condition' => GuardInterface::CONDITION_OR, + ], + new \ArrayIterator([ + 'controller' => 'MyController2', + 'actions' => new \ArrayIterator(['edit', 'DELETE']), + 'permissions' => new \ArrayIterator(['permission2']), + 'condition' => GuardInterface::CONDITION_OR, + ]) + ], + 'expected' => [ + 'mycontroller' => [ + 'edit' => ['permission1'], + 'delete' => ['permission1'], + 1 => GuardInterface::CONDITION_OR, + ], + 'mycontroller2' => [ + 'edit' => ['permission2'], + 'delete' => ['permission2'], + 1 => GuardInterface::CONDITION_OR, ] ] ], @@ -141,17 +237,43 @@ public function rulesConversionProvider() [ 'controller' => 'MyController', 'actions' => ['edit'], - 'permissions' => 'permission1' + 'permissions' => 'permission1', ], [ 'controller' => 'MyController', - 'permissions' => 'permission2' + 'permissions' => 'permission2', + ] + ], + 'expected' => [ + 'mycontroller' => [ + 'edit' => ['permission1'], + 0 => ['permission2'], + 1 => GuardInterface::CONDITION_AND, + ] + ] + ], + // Test that that if a rule is set globally to the controller, it does not override any + // action specific rule that may have been specified before + // OR condition + [ + 'rules' => [ + [ + 'controller' => 'MyController', + 'actions' => ['edit'], + 'permissions' => 'permission1', + 'condition' => GuardInterface::CONDITION_OR + ], + [ + 'controller' => 'MyController', + 'permissions' => 'permission2', + 'condition' => GuardInterface::CONDITION_OR ] ], 'expected' => [ 'mycontroller' => [ 'edit' => ['permission1'], - 0 => ['permission2'] + 0 => ['permission2'], + 1 => GuardInterface::CONDITION_OR, ] ] ]