Skip to content

Commit

Permalink
Merge branch 'develop' into feature/HMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
tswagger committed Aug 23, 2023
2 parents f9df01c + 970c67e commit 24db9e6
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 32 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
3 changes: 2 additions & 1 deletion src/Config/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ class Auth extends BaseConfig
*/
public function loginRedirect(): string
{
$url = setting('Auth.redirects')['login'];
$session = session();
$url = $session->getTempdata('beforeLoginUrl') ?? setting('Auth.redirects')['login'];

return $this->getUrl($url);
}
Expand Down
5 changes: 5 additions & 0 deletions src/Filters/SessionAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public function before(RequestInterface $request, $arguments = null)
->with('error', $authenticator->getPendingMessage());
}

if (! url_is('login')) {
$session = session();
$session->setTempdata('beforeLoginUrl', current_url(), 300);
}

return redirect()->route('login');
}

Expand Down
11 changes: 11 additions & 0 deletions tests/Authentication/Filters/SessionFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,15 @@ public function testBlocksInactiveUsers(): void

setting('Auth.actions', ['register' => null]);
}

public function testStoreRedirectsToEntraceUrlIntoSession(): void
{
$result = $this->call('get', 'protected-route');

$result->assertRedirectTo('/login');

$session = session();
$this->assertNotEmpty($session->get('beforeLoginUrl'));
$this->assertSame(site_url('protected-route'), $session->get('beforeLoginUrl'));
}
}
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 24db9e6

Please sign in to comment.