diff --git a/.github/workflows/no-merge-commits.yml b/.github/workflows/no-merge-commits.yml index 45ea8c2fe..4a63e55e9 100644 --- a/.github/workflows/no-merge-commits.yml +++ b/.github/workflows/no-merge-commits.yml @@ -19,6 +19,6 @@ jobs: uses: actions/checkout@v4 - name: Run test - uses: NexusPHP/no-merge-commits@v2.2.1 + uses: NexusPHP/no-merge-commits@v2.1.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/customization/user_provider.md b/docs/customization/user_provider.md index 8ae742e19..379574e80 100644 --- a/docs/customization/user_provider.md +++ b/docs/customization/user_provider.md @@ -54,17 +54,3 @@ class UserModel extends ShieldUserModel } } ``` - -## Creating a Custom User Entity - -Starting from v1.2.0, `UserModel` in Shield has the `createNewUser()` method to -create a new User Entity. - -```php -$user = $userModel->createNewUser($data); -``` - -It takes an optional user data array as the first argument, and passes it to the -constructor of the `$returnType` class. - -If your custom User entity cannot be instantiated in this way, override this method. diff --git a/docs/references/authentication/hmac.md b/docs/references/authentication/hmac.md index 737199cbf..fed1f0bb7 100644 --- a/docs/references/authentication/hmac.md +++ b/docs/references/authentication/hmac.md @@ -119,14 +119,14 @@ permissions the token grants to the user. Scopes are provided when the token is cannot be modified afterword. ```php -$token = $user->generateHmacToken('Work Laptop', ['posts.manage', 'forums.manage']); +$token = $user->gererateHmacToken('Work Laptop', ['posts.manage', 'forums.manage']); ``` By default, a user is granted a wildcard scope which provides access to all scopes. This is the same as: ```php -$token = $user->generateHmacToken('Work Laptop', ['*']); +$token = $user->gererateHmacToken('Work Laptop', ['*']); ``` During authentication, the HMAC Keys the user used is stored on the user. Once authenticated, you diff --git a/docs/user_management/forcing_password_reset.md b/docs/user_management/forcing_password_reset.md index 97237053c..2fae5f98e 100644 --- a/docs/user_management/forcing_password_reset.md +++ b/docs/user_management/forcing_password_reset.md @@ -38,7 +38,7 @@ if ($user->requiresPasswordReset()) { !!! note - You can use the [force-reset](../references/controller_filters.md/#forcing-password-reset) + You can use the [force-reset](../references/controller_filters/#forcing-password-reset) filter to check. ### Force Password Reset On a User 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/Collectors/Auth.php b/src/Collectors/Auth.php index a0e8331b1..a1824d4bd 100644 --- a/src/Collectors/Auth.php +++ b/src/Collectors/Auth.php @@ -83,7 +83,7 @@ public function display(): string $html = '

Current User

'; $html .= ''; - $html .= ""; + $html .= ""; $html .= ""; $html .= ""; $html .= ""; diff --git a/src/Controllers/RegisterController.php b/src/Controllers/RegisterController.php index b85e90082..7b310b1f8 100644 --- a/src/Controllers/RegisterController.php +++ b/src/Controllers/RegisterController.php @@ -103,7 +103,8 @@ public function registerAction(): RedirectResponse // Save the user $allowedPostFields = array_keys($rules); - $user = $users->createNewUser($this->request->getPost($allowedPostFields)); + $user = $this->getUserEntity(); + $user->fill($this->request->getPost($allowedPostFields)); // Workaround for email only registration/login if ($user->username === null) { @@ -159,14 +160,10 @@ protected function getUserProvider(): UserModel /** * Returns the Entity class that should be used - * - * @deprecated 1.2.0 No longer used. */ protected function getUserEntity(): User { - $userProvider = $this->getUserProvider(); - - return $userProvider->createNewUser(); + return new User(); } /** 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/src/Models/UserModel.php b/src/Models/UserModel.php index 8eae4d66a..0fede61ec 100644 --- a/src/Models/UserModel.php +++ b/src/Models/UserModel.php @@ -397,14 +397,4 @@ private function checkReturnType(): void throw new LogicException('Return type must be a subclass of ' . User::class); } } - - /** - * Returns a new User Entity. - * - * @param array|bool|float|int|object|string|null> $data (Optional) user data - */ - public function createNewUser(array $data = []): User - { - return new $this->returnType($data); - } } 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')); + } }
User ID#{$user->id}
User ID#{$user->id}
Username{$user->username}
Email{$user->email}
Groups{$groupsForUser}