diff --git a/src/Authorization/Traits/Authorizable.php b/src/Authorization/Traits/Authorizable.php index c0b1acf2a..72fd527a3 100644 --- a/src/Authorization/Traits/Authorizable.php +++ b/src/Authorization/Traits/Authorizable.php @@ -258,7 +258,7 @@ 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 + . ' Invalid permission: ' . $permission ); } @@ -280,8 +280,14 @@ public function can(string ...$permissions): bool } // Check wildcard match - $check = substr($permission, 0, strpos($permission, '.')) . '.*'; - if (isset($matrix[$group]) && in_array($check, $matrix[$group], true)) { + $checks = []; + $parts = explode('.', $permission); + + for ($i = count($parts); $i > 0; $i--) { + $check = implode('.', array_slice($parts, 0, $i)) . '.*'; + $checks[] = $check; + } + if (isset($matrix[$group]) && array_intersect($checks, $matrix[$group]) !== []) { return true; } } diff --git a/src/Entities/Group.php b/src/Entities/Group.php index b63707929..5b417a6b2 100644 --- a/src/Entities/Group.php +++ b/src/Entities/Group.php @@ -85,9 +85,17 @@ public function can(string $permission): bool } // Check wildcard match - $check = substr($permission, 0, strpos($permission, '.')) . '.*'; + $checks = []; + $parts = explode('.', $permission); - return $this->permissions !== null && $this->permissions !== [] && in_array($check, $this->permissions, true); + for ($i = count($parts); $i > 0; $i--) { + $check = implode('.', array_slice($parts, 0, $i)) . '.*'; + $checks[] = $check; + } + + return $this->permissions !== null + && $this->permissions !== [] + && array_intersect($checks, $this->permissions) !== []; } /** diff --git a/tests/Authorization/GroupTest.php b/tests/Authorization/GroupTest.php index 68c190be8..e3479c9fa 100644 --- a/tests/Authorization/GroupTest.php +++ b/tests/Authorization/GroupTest.php @@ -87,4 +87,28 @@ public function testCan(): void $this->assertTrue($group2->can('users.edit')); $this->assertFalse($group2->can('foo.bar')); } + + public function testCanNestedPerms(): void + { + $group = $this->groups->info('user'); + + $group->addPermission('foo.bar.*'); + $group->addPermission('foo.biz.buz.*'); + + $this->assertTrue($group->can('foo.bar')); + $this->assertTrue($group->can('foo.bar.*')); + $this->assertTrue($group->can('foo.bar.baz')); + $this->assertTrue($group->can('foo.bar.buz')); + $this->assertTrue($group->can('foo.bar.buz.biz')); + $this->assertTrue($group->can('foo.biz.buz')); + $this->assertTrue($group->can('foo.biz.buz.*')); + $this->assertTrue($group->can('foo.biz.buz.bar')); + $this->assertFalse($group->can('foo')); + $this->assertFalse($group->can('foo.*')); + $this->assertFalse($group->can('foo.biz')); + $this->assertFalse($group->can('foo.buz')); + $this->assertFalse($group->can('foo.biz.*')); + $this->assertFalse($group->can('foo.biz.bar')); + $this->assertFalse($group->can('foo.biz.bar.buz')); + } }