Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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