diff --git a/docs/authorization.md b/docs/authorization.md index ee3aa104c..7265ce6c2 100644 --- a/docs/authorization.md +++ b/docs/authorization.md @@ -111,7 +111,7 @@ The `Authorizable` trait on the `User` entity provides the following methods to #### can() -Allows you to check if a user is permitted to do a specific action. The only argument is the permission string. Returns +Allows you to check if a user is permitted to do a specific action or group or actions. The permission string(s) should be passed as the argument(s). Returns boolean `true`/`false`. Will check the user's direct permissions (**user-level permissions**) first, and then check against all of the user's groups permissions (**group-level permissions**) to determine if they are allowed. @@ -119,6 +119,11 @@ permissions (**group-level permissions**) to determine if they are allowed. if ($user->can('users.create')) { // } + +// If multiple permissions are specified, true is returned if the user has any of them. +if ($user->can('users.create', 'users.edit')) { + // +} ``` #### inGroup() diff --git a/src/Authorization/Traits/Authorizable.php b/src/Authorization/Traits/Authorizable.php index 9e3f32ff1..6cee326ce 100644 --- a/src/Authorization/Traits/Authorizable.php +++ b/src/Authorization/Traits/Authorizable.php @@ -226,49 +226,54 @@ public function hasPermission(string $permission): bool /** * Checks user permissions and their group permissions - * to see if the user has a specific permission. + * to see if the user has a specific permission or group + * of permissions. * - * @param string $permission string consisting of a scope and action, like `users.create` + * @param string $permissions string(s) consisting of a scope and action, like `users.create` */ - public function can(string $permission): bool + public function can(string ...$permissions): bool { - if (strpos($permission, '.') === false) { - throw new LogicException( - 'A permission must be a string consisting of a scope and action, like `users.create`.' - . ' Invalid permission: ' . $permission - ); - } - + // Get user's permissions and store in cache $this->populatePermissions(); - $permission = strtolower($permission); - - // Check user's permissions - if (in_array($permission, $this->permissionsCache, true)) { - return true; - } - // Check the groups the user belongs to $this->populateGroups(); - if (! count($this->groupCache)) { - return false; - } + foreach ($permissions as $permission) { + // Permission must contain a scope and action + if (strpos($permission, '.') === false) { + throw new LogicException( + 'A permission must be a string consisting of a scope and action, like `users.create`.' + . ' Invalid permission: ' . $permission + ); + } - $matrix = function_exists('setting') - ? setting('AuthGroups.matrix') - : config('AuthGroups')->matrix; + $permission = strtolower($permission); - foreach ($this->groupCache as $group) { - // Check exact match - if (isset($matrix[$group]) && in_array($permission, $matrix[$group], true)) { + // Check user's permissions + if (in_array($permission, $this->permissionsCache, true)) { return true; } - // Check wildcard match - $check = substr($permission, 0, strpos($permission, '.')) . '.*'; - if (isset($matrix[$group]) && in_array($check, $matrix[$group], true)) { - return true; + if (! count($this->groupCache)) { + return false; + } + + $matrix = function_exists('setting') + ? setting('AuthGroups.matrix') + : config('AuthGroups')->matrix; + + foreach ($this->groupCache as $group) { + // Check exact match + if (isset($matrix[$group]) && in_array($permission, $matrix[$group], true)) { + return true; + } + + // Check wildcard match + $check = substr($permission, 0, strpos($permission, '.')) . '.*'; + if (isset($matrix[$group]) && in_array($check, $matrix[$group], true)) { + return true; + } } } diff --git a/tests/Authorization/AuthorizableTest.php b/tests/Authorization/AuthorizableTest.php index 464cfcf1c..1dc098abf 100644 --- a/tests/Authorization/AuthorizableTest.php +++ b/tests/Authorization/AuthorizableTest.php @@ -305,6 +305,28 @@ public function testCanGetsInvalidPermission(): void $this->assertTrue($this->user->can('developer')); } + /** + * @see https://github.com/codeigniter4/shield/pull/791#discussion_r1297712860 + */ + public function testCanWorksWithMultiplePermissions(): void + { + // Check for user's direct permissions (user-level permissions) + $this->user->addPermission('users.create', 'users.edit'); + + $this->assertTrue($this->user->can('users.create', 'users.edit')); + $this->assertFalse($this->user->can('beta.access', 'admin.access')); + + $this->user->removePermission('users.create', 'users.edit'); + + $this->assertFalse($this->user->can('users.edit', 'users.create')); + + // Check for user's group permissions (group-level permissions) + $this->user->addGroup('superadmin'); + + $this->assertTrue($this->user->can('admin.access', 'beta.access')); + $this->assertTrue($this->user->can('admin.*', 'users.*')); + } + /** * @see https://github.com/codeigniter4/shield/pull/238 */