Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for multiple permissions check for users #791

Merged
merged 7 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,18 @@ 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')) {
//
}
// Or
sammyskills marked this conversation as resolved.
Show resolved Hide resolved
if ($user->can('users.create', 'users.edit')) {
//
}
```

#### inGroup()
Expand Down
70 changes: 37 additions & 33 deletions src/Authorization/Traits/Authorizable.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,49 +226,53 @@ 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
);
}

$this->populatePermissions();

$permission = strtolower($permission);
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
);
}

// Check user's permissions
if (in_array($permission, $this->permissionsCache, true)) {
return true;
}
$this->populatePermissions();
kenjis marked this conversation as resolved.
Show resolved Hide resolved

// Check the groups the user belongs to
$this->populateGroups();
$permission = strtolower($permission);

if (! count($this->groupCache)) {
return false;
}
// Check user's permissions
if (in_array($permission, $this->permissionsCache, true)) {
return true;
}

$matrix = function_exists('setting')
? setting('AuthGroups.matrix')
: config('AuthGroups')->matrix;
// Check the groups the user belongs to
$this->populateGroups();

foreach ($this->groupCache as $group) {
// Check exact match
if (isset($matrix[$group]) && in_array($permission, $matrix[$group], true)) {
return true;
if (! count($this->groupCache)) {
return false;
}

// Check wildcard match
$check = substr($permission, 0, strpos($permission, '.')) . '.*';
if (isset($matrix[$group]) && in_array($check, $matrix[$group], true)) {
return true;
$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
21 changes: 21 additions & 0 deletions tests/Authorization/AuthorizableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,27 @@ 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'));
}

/**
* @see https://github.com/codeigniter4/shield/pull/238
*/
Expand Down
Loading