Skip to content

Commit

Permalink
Merge pull request #791 from sammyskills/support-multiple-permissions
Browse files Browse the repository at this point in the history
feat: add support for multiple permissions check for users
  • Loading branch information
kenjis authored Aug 22, 2023
2 parents 78cac54 + 6d9e5be commit f334a92
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 31 deletions.
7 changes: 6 additions & 1 deletion docs/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,19 @@ 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.

```php
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()
Expand Down
65 changes: 35 additions & 30 deletions src/Authorization/Traits/Authorizable.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions tests/Authorization/AuthorizableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down

0 comments on commit f334a92

Please sign in to comment.