diff --git a/README.md b/README.md index 7eeb4ce..f6989bb 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ php artisan vendor:publish --provider="Playground\Auth\ServiceProvider" --tag="p See the contents of the published config file: [config/playground-auth.php](config/playground-auth.php) The default configuration utitlizes: -- Sanctum with role based abilities +- Sanctum - Users may have additional abilities in the [`Playground\Models\User`](https://github.com/gammamatrix/playground/blob/develop/src/Models/User.php): `users.abilities` - The Playground user model uses a UUID primary key along with additional fields. See the [migration for `Playground\Models\User`](https://github.com/gammamatrix/playground/blob/develop/database/migrations-playground/2014_10_12_000000_create_users_table.php) @@ -86,9 +86,12 @@ Here is an example of the configurable abilities: 'playground-auth:reset-password', // ... ``` +- Packages may also be enabled to load privileges with the [PLAYGROUND_AUTH_PACKAGES environment variable.](https://github.com/gammamatrix/playground-auth/wiki/Environment-Variables) ### Environment Variables +Read more about [`playground-auth` environment variables](https://github.com/gammamatrix/playground-auth/wiki/Environment-Variables) on the [`playground-auth` wiki](https://github.com/gammamatrix/playground-auth/wiki/). + ### User model types Playground tests many different User model types to support any ecosystem. @@ -99,7 +102,8 @@ Make sure your app is configured for the proper user model in the Laravel config 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => App\Models\User::class, + // 'model' => App\Models\User::class, + 'model' => env('AUTH_PROVIDERS_USERS_MODEL', App\Models\User::class), ], ``` @@ -108,15 +112,6 @@ During testing, Playground tests user various models. config(['auth.providers.users.model' => 'Playground\\Models\\User']) ``` - -#### Loading - -| env() | config() | -|-------------------------------------|-------------------------------------| -| `PLAYGROUND_AUTH_LOAD_COMMANDS` | `playground-auth.load.commands` | -| `PLAYGROUND_AUTH_LOAD_TRANSLATIONS` | `playground-auth.load.translations` | - - ## Commands This package adds a command to hash a password from the command line: diff --git a/config/playground-auth.php b/config/playground-auth.php index acd158c..5cced3e 100644 --- a/config/playground-auth.php +++ b/config/playground-auth.php @@ -1,39 +1,213 @@ env('PLAYGROUND_AUTH_REDIRECT', null), - // 'session' => false, - 'token' => [ - // 'abilities' => '', - // 'abilities' => 'user', - 'abilities' => 'merge', - 'expires' => 'tomorrow midnight', - // 'expires' => null, - 'name' => 'app', - // @see playground.auth.token.name - 'listed' => true, - 'roles' => false, - 'privileges' => false, - 'sanctum' => true, + + /* + |-------------------------------------------------------------------------- + | Packages + |-------------------------------------------------------------------------- + | + | PLAYGROUND_AUTH_PACKAGES may be used to load abilities from other packages. + | + | PLAYGROUND_AUTH_REQUIRE_PACKAGE_ABILITIES is enabled by default. When + | using token abilities, this value ensures that `playground-auth` is + | included in `PLAYGROUND_AUTH_PACKAGES` if omitted in the .env. + | + | PLAYGROUND_AUTH_DEBUG Requires config(app.debug) to be true to display logs. + */ + + 'packages' => is_string(env('PLAYGROUND_AUTH_PACKAGES', 'playground-auth')) ? array_map( + 'trim', + explode(',', env('PLAYGROUND_AUTH_PACKAGES', 'playground-auth')) + ) : [], + + 'require' => [ + /** + * @var bool package_abilities By default, require + */ + 'package_abilities' => (bool) env('PLAYGROUND_AUTH_REQUIRE_PACKAGE_ABILITIES', true), ], + + /** + * @var bool debug Enable authentication debugging messages. + */ + 'debug' => (bool) env('PLAYGROUND_AUTH_DEBUG', false), + // 'debug' => (bool) env('PLAYGROUND_AUTH_DEBUG', true), + + /* + |-------------------------------------------------------------------------- + | Loading + |-------------------------------------------------------------------------- + | + | PLAYGROUND_AUTH_LOAD_COMMANDS enables Console\Commands\HashPassword + | + | PLAYGROUND_AUTH_LOAD_TRANSLATIONS loads translations in /lang + | + */ + 'load' => [ 'commands' => (bool) env('PLAYGROUND_AUTH_LOAD_COMMANDS', true), 'translations' => (bool) env('PLAYGROUND_AUTH_LOAD_TRANSLATIONS', true), ], + + /* + |-------------------------------------------------------------------------- + | Redirects + |-------------------------------------------------------------------------- + | + | PLAYGROUND_AUTH_REDIRECT may be disabled to show an error page instead. + */ + + /** + * @var ?string redirect Specify for redirect()->guest($redirect) + */ + 'redirect' => env('PLAYGROUND_AUTH_REDIRECT', 'login'), + // 'redirect' => null, + + /* + |-------------------------------------------------------------------------- + | Policies + |-------------------------------------------------------------------------- + | + | ModelPolicy supports multiple security implementations. + | + | Options for Sanctum: + | - PLAYGROUND_AUTH_USER_PRIVILEGES - allow saving privileges in the user model. + | - PLAYGROUND_AUTH_VERIFY === privileges + | + */ + + /** + * @var string verify user|privileges|roles + */ + 'verify' => env('PLAYGROUND_AUTH_VERIFY', 'privileges'), + + /** + * @var bool sanctum Enable Sanctum + */ + 'sanctum' => (bool) env('PLAYGROUND_AUTH_SANCTUM', true), + /** - * Provide an array of email addresses for admin privileges. + * @var bool hasPrivilege Enable if the user model has $user->hasPrivilege($privilege) + */ + 'hasPrivilege' => (bool) env('PLAYGROUND_AUTH_HAS_PRIVILEGE', false), + + /** + * @var bool hasPrivilege Enable if the user model has the attribute User::$privileges + */ + 'userPrivileges' => (bool) env('PLAYGROUND_AUTH_USER_PRIVILEGES', false), + + /** + * @var bool hasRole Enable if the user model has $user->hasRole($role) + */ + 'hasRole' => (bool) env('PLAYGROUND_AUTH_HAS_ROLE', false), + // 'hasRole' => (bool) env('PLAYGROUND_AUTH_HAS_ROLE', true), + + /** + * @var bool userRole Enable if the user model has the attribute User::$role + */ + 'userRole' => (bool) env('PLAYGROUND_AUTH_USER_ROLE', false), + // 'userRole' => (bool) env('PLAYGROUND_AUTH_USER_ROLE', true), + + /** + * @var bool userRoles Enable if the user model has the attribute User::$roles + */ + 'userRoles' => (bool) env('PLAYGROUND_AUTH_USER_ROLES', false), + // 'userRoles' => (bool) env('PLAYGROUND_AUTH_USER_ROLES', true), + + /* + |-------------------------------------------------------------------------- + | Token configuration + |-------------------------------------------------------------------------- + | + | Enabling Sanctum provides token and API key support. + | + */ + + 'token' => [ + + /** + * @var string abilities merge|user + */ + 'abilities' => env('PLAYGROUND_AUTH_TOKEN_ABILITIES', 'merge'), + + /** + * @var ?string expires Set expires to null to allow tokens to live forever. + */ + 'expires' => env('PLAYGROUND_AUTH_TOKEN_EXPIRES', 'tomorrow midnight'), + + /** + * @var string name The token name. + */ + 'name' => env('PLAYGROUND_AUTH_TOKEN_NAME', 'app'), + + /** + * @var bool listed Use the listed admins and managers in this configuration. + */ + 'listed' => (bool) env('PLAYGROUND_AUTH_TOKEN_LISTED', false), + + /** + * @var bool roles Check the user role(s) for applying abilities. + */ + 'roles' => (bool) env('PLAYGROUND_AUTH_TOKEN_ROLES', false), + // 'roles' => (bool) env('PLAYGROUND_AUTH_TOKEN_ROLES', true), + + /** + * @var bool privileges Allow the attribute User::$privileges to be used for authorization. + */ + 'privileges' => (bool) env('PLAYGROUND_AUTH_TOKEN_PRIVILEGES', false), + // 'privileges' => (bool) env('PLAYGROUND_AUTH_TOKEN_PRIVILEGES', true), + + /** + * @var bool sanctum The token will use Sanctum. + */ + 'sanctum' => (bool) env('PLAYGROUND_AUTH_TOKEN_SANCTUM', true), + // 'sanctum' => (bool) env('PLAYGROUND_AUTH_TOKEN_SANCTUM', false), + + ], + + /* + |-------------------------------------------------------------------------- + | Listed admins and managers + |-------------------------------------------------------------------------- + | + | Allow specifying a set of admins and/or managers. + | + */ + + /** + * @var array admins Provide an array of email addresses for admin privileges. */ 'admins' => [ // 'cindy@example.com', // 'joe@example.com', // 'tim@example.com', ], + /** - * Provide an array of email addresses for manager privileges. + * @var array managers Provide an array of email addresses for manager privileges. */ 'managers' => [ // 'sally@example.com', ], + + /* + |-------------------------------------------------------------------------- + | Abilities + |-------------------------------------------------------------------------- + | + | Root: has all privileges, where applicable. + | + | Admins: have wildcard access, at top level of resources. + | + | Manager: Has wildcard access at the model level. + | + | User: Has specific privileges and no wildcards. + | + | Guest: Specify `deny` for no privileges. + | + */ + 'abilities' => [ 'root' => [ '*', @@ -42,8 +216,6 @@ 'app:*', 'playground:*', 'playground-auth:*', - 'playground-matrix:*', - 'playground-matrix-resource:*', ], 'manager' => [ 'app:view', @@ -52,25 +224,6 @@ 'playground-auth:logout', 'playground-auth:reset-password', - - 'playground-matrix:view', - 'playground-matrix-resource:view', - - 'playground-matrix-resource:backlog:*', - 'playground-matrix-resource:board:*', - 'playground-matrix-resource:epic:*', - 'playground-matrix-resource:flow:*', - 'playground-matrix-resource:milestone:*', - 'playground-matrix-resource:note:*', - 'playground-matrix-resource:project:*', - 'playground-matrix-resource:release:*', - 'playground-matrix-resource:roadmap:*', - 'playground-matrix-resource:source:*', - 'playground-matrix-resource:sprint:*', - 'playground-matrix-resource:tag:*', - 'playground-matrix-resource:team:*', - 'playground-matrix-resource:ticket:*', - 'playground-matrix-resource:version:*', ], 'user' => [ 'app:view', @@ -79,47 +232,9 @@ 'playground-auth:logout', 'playground-auth:reset-password', - - 'playground-matrix:view', - 'playground-matrix-resource:view', - - 'playground-matrix-resource:backlog:view', - 'playground-matrix-resource:backlog:viewAny', - 'playground-matrix-resource:board:view', - 'playground-matrix-resource:board:viewAny', - 'playground-matrix-resource:epic:view', - 'playground-matrix-resource:epic:viewAny', - 'playground-matrix-resource:flow:view', - 'playground-matrix-resource:flow:viewAny', - 'playground-matrix-resource:milestone:view', - 'playground-matrix-resource:milestone:viewAny', - 'playground-matrix-resource:note:view', - 'playground-matrix-resource:note:viewAny', - 'playground-matrix-resource:project:view', - 'playground-matrix-resource:project:viewAny', - 'playground-matrix-resource:release:view', - 'playground-matrix-resource:release:viewAny', - 'playground-matrix-resource:roadmap:view', - 'playground-matrix-resource:roadmap:viewAny', - 'playground-matrix-resource:source:view', - 'playground-matrix-resource:source:viewAny', - 'playground-matrix-resource:sprint:view', - 'playground-matrix-resource:sprint:viewAny', - 'playground-matrix-resource:tag:view', - 'playground-matrix-resource:tag:viewAny', - 'playground-matrix-resource:team:view', - 'playground-matrix-resource:team:viewAny', - 'playground-matrix-resource:ticket:view', - 'playground-matrix-resource:ticket:viewAny', - 'playground-matrix-resource:ticket:create', - 'playground-matrix-resource:ticket:edit', - 'playground-matrix-resource:ticket:store', - 'playground-matrix-resource:ticket:update', - 'playground-matrix-resource:version:view', - 'playground-matrix-resource:version:viewAny', ], 'guest' => [ - 'none', + 'deny', ], // 'guest' => [ // 'app:view', @@ -128,37 +243,6 @@ // 'playground-auth:logout', // 'playground-auth:reset-password', - - // 'playground-matrix-resource:backlog:view', - // 'playground-matrix-resource:backlog:viewAny', - // 'playground-matrix-resource:board:view', - // 'playground-matrix-resource:board:viewAny', - // 'playground-matrix-resource:epic:view', - // 'playground-matrix-resource:epic:viewAny', - // 'playground-matrix-resource:flow:view', - // 'playground-matrix-resource:flow:viewAny', - // 'playground-matrix-resource:milestone:view', - // 'playground-matrix-resource:milestone:viewAny', - // 'playground-matrix-resource:note:view', - // 'playground-matrix-resource:note:viewAny', - // 'playground-matrix-resource:project:view', - // 'playground-matrix-resource:project:viewAny', - // 'playground-matrix-resource:release:view', - // 'playground-matrix-resource:release:viewAny', - // 'playground-matrix-resource:roadmap:view', - // 'playground-matrix-resource:roadmap:viewAny', - // 'playground-matrix-resource:source:view', - // 'playground-matrix-resource:source:viewAny', - // 'playground-matrix-resource:sprint:view', - // 'playground-matrix-resource:sprint:viewAny', - // 'playground-matrix-resource:tag:view', - // 'playground-matrix-resource:tag:viewAny', - // 'playground-matrix-resource:team:view', - // 'playground-matrix-resource:team:viewAny', - // 'playground-matrix-resource:ticket:view', - // 'playground-matrix-resource:ticket:viewAny', - // 'playground-matrix-resource:version:view', - // 'playground-matrix-resource:version:viewAny', // ], ], ]; diff --git a/lang/en/auth.php b/lang/en/auth.php index c97ab61..5d1e3bd 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -13,10 +13,21 @@ | */ + // 401 - Unauthorized + 'unauthorized' => 'Unauthorized', + // 403 - Forbidden + 'forbidden' => 'Forbidden', + // 406 - Not Acceptable + 'unacceptable' => 'Not Acceptable', + + 'model.locked' => 'The :model record is locked. You must unlock it to make changes.', + 'failed' => 'These credentials do not match our records.', 'password' => 'The provided password is incorrect.', - 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + 'permission' => 'You do not have permission.', 'required' => 'Authentication is required. Please log in.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 'sanctum.disabled' => 'Sorry, Sanctum authenticaction is currently disabled.', + ]; diff --git a/resources/docs/artisan-about-playground-auth.png b/resources/docs/artisan-about-playground-auth.png index 89fad01..ea73d1a 100644 Binary files a/resources/docs/artisan-about-playground-auth.png and b/resources/docs/artisan-about-playground-auth.png differ diff --git a/src/Issuer.php b/src/Issuer.php index 58203ed..9a05083 100644 --- a/src/Issuer.php +++ b/src/Issuer.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Carbon; // use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; use Laravel\Sanctum\Contracts\HasApiTokens; use Playground\Models\Contracts\Abilities; use Playground\Models\Contracts\Admin; @@ -45,14 +46,159 @@ class Issuer protected bool $onlyUserAbilities = false; + /** + * @var array + */ + protected array $rootAccessGroups = [ + 'root', + 'admin', + ]; + + /** + * @var array + */ + protected array $rootAbilities = [ + '*', + 'root', + ]; + /** * @return array */ - protected function abilitiesByGroup(string $group): array + protected function compileAbilitiesByGroup(string $group): array { - $abilities = config('playground-auth.abilities.'.$group); + /** + * @var array $packages + */ + $packages = config('playground-auth.packages'); - return is_array($abilities) ? $abilities : []; + if (! empty(config('playground-auth.require.package_abilities'))) { + if (! in_array('playground-auth', $packages)) { + array_unshift($packages, 'playground-auth'); + } + } + + /** + * @var array> $package_abilities + */ + $package_abilities = []; + + /** + * @var array $abilities + */ + $abilities = []; + + if (empty($group)) { + // Set the lowest permission scheme for abilities. + $group = 'guest'; + } + + if (! empty($packages) && is_array($packages)) { + foreach ($packages as $package) { + $config = config($package.'.abilities.'.$group); + $package_abilities[$package] = is_array($config) ? $config : []; + } + } + + foreach ($package_abilities as $package => $list) { + if (! empty($list)) { + foreach ($list as $ability) { + if (empty($ability) || ! is_string($ability)) { + if (config('app.debug') && config('playground-auth.debug')) { + Log::debug(__METHOD__, [ + 'error' => 'Invalid abilities defined in package.', + '$ability' => $ability, + '$group' => $group, + '$package' => $package, + '$list' => $list, + ]); + } + + continue; + } + if (in_array('deny', $abilities)) { + if ($package !== 'playground-auth') { + if (config('app.debug') && config('playground-auth.debug')) { + Log::debug(__METHOD__, [ + 'error' => 'Only the playground-auth configuration may implement deny for a group.', + '$ability' => $ability, + '$group' => $group, + '$package' => $package, + '$list' => $list, + ]); + } + + continue; + } + $abilities = [ + 'deny', + ]; + } + if ($ability === 'deny') { + if ($package !== 'playground-auth') { + if (config('app.debug') && config('playground-auth.debug')) { + Log::debug(__METHOD__, [ + 'error' => 'Only the playground-auth configuration may implement deny for a group.', + '$ability' => $ability, + '$group' => $group, + '$package' => $package, + '$list' => $list, + ]); + } + + continue; + } + $abilities = [ + 'deny', + ]; + } + if (in_array($ability, $this->rootAbilities) + && ! in_array($group, $this->rootAccessGroups) + ) { + if (config('app.debug') && config('playground-auth.debug')) { + Log::debug(__METHOD__, [ + 'error' => sprintf('Root abilites are limited to the groups: %1$s', implode(', ', $this->rootAccessGroups)), + '$ability' => $ability, + '$group' => $group, + '$package' => $package, + '$list' => $list, + '$this->rootAccessGroups' => $this->rootAccessGroups, + '$this->rootAbilities' => $this->rootAbilities, + ]); + + } + + continue; + } + if (! in_array($ability, $abilities)) { + $abilities[] = $ability; + } + } + } + } + + // dump([ + // '__METHOD__' => __METHOD__, + // '$group' => $group, + // '$abilities' => $abilities, + // '$package_abilities' => $package_abilities, + // '$this->rootAccessGroups' => $this->rootAccessGroups, + // '$this->rootAbilities' => $this->rootAbilities, + // '$this->isRoot' => $this->isRoot, + // '$this->isAdmin' => $this->isAdmin, + // '$this->isManager' => $this->isManager, + // '$this->isUser' => $this->isUser, + // '$this->isGuest' => $this->isGuest, + // ]); + return $abilities; + } + + /** + * @return array + */ + protected function abilitiesByGroup(string $group): array + { + return $this->compileAbilitiesByGroup($group); } /** @@ -86,7 +232,7 @@ protected function abilities(Authenticatable $user): array } if (empty($this->abilities)) { - $this->abilities[] = 'none'; + // $this->abilities[] = 'deny'; } return $this->abilities; @@ -102,6 +248,11 @@ public function init(Authenticatable $user, array $config): void } else { $this->hasSanctum = false; } + // dump([ + // '__METHOD__' => __METHOD__, + // '$user' => $user->toArray(), + // '$config' => $config, + // ]); if ($user instanceof Abilities) { if (empty($config['abilities'])) { diff --git a/src/Policies/ModelPolicy.php b/src/Policies/ModelPolicy.php index 5c53bfd..695e59e 100644 --- a/src/Policies/ModelPolicy.php +++ b/src/Policies/ModelPolicy.php @@ -35,7 +35,7 @@ public function delete( // NOTE: This lock check is bypassed by a root user. if ($model->getAttribute('locked')) { // return Response::denyWithStatus(423); - return Response::denyWithStatus(423, __('playground::auth.model.locked', [ + return Response::denyWithStatus(423, __('playground-auth::auth.model.locked', [ 'model' => Str::of(class_basename($model)) ->snake()->replace('_', ' ')->title()->lower(), ])); @@ -111,7 +111,7 @@ public function update(Authenticatable $user, Model $model): bool|Response // NOTE: This lock check is bypassed by a root user. if ($model->getAttribute('locked')) { // return Response::denyWithStatus(423); - return Response::denyWithStatus(423, __('playground::auth.model.locked', [ + return Response::denyWithStatus(423, __('playground-auth::auth.model.locked', [ 'model' => Str::of(class_basename($model))->snake()->replace('_', ' ')->title()->lower(), ])); } diff --git a/src/Policies/Policy.php b/src/Policies/Policy.php index d64401a..cbf2d03 100644 --- a/src/Policies/Policy.php +++ b/src/Policies/Policy.php @@ -45,12 +45,12 @@ public function before(Authenticatable $user, $ability) // '$ability' => $ability, // '$this->allowRootOverride' => $this->allowRootOverride, // ]); - // dd([ + // dump([ // '__METHOD__' => __METHOD__, // '__FILE__' => __FILE__, // '__LINE__' => __LINE__, // 'static::class' => static::class, - // '$user' => $user ? $user->toArray(): $user, + // '$user' => $user->toArray(), // '$ability' => $ability, // '$this->allowRootOverride' => $this->allowRootOverride, // '$this->package' => $this->package, @@ -74,6 +74,17 @@ public function before(Authenticatable $user, $ability) */ public function index(Authenticatable $user): bool|Response { + // dump([ + // '__METHOD__' => __METHOD__, + // '__FILE__' => __FILE__, + // '__LINE__' => __LINE__, + // 'static::class' => static::class, + // '$user' => $user->toArray(), + // '$this->allowRootOverride' => $this->allowRootOverride, + // '$this->package' => $this->package, + // '$this->entity' => $this->entity, + // ]); + // \Log::debug(__METHOD__, [ // '$user' => $user, // ]); diff --git a/src/Policies/PolicyTrait.php b/src/Policies/PolicyTrait.php index 1584730..69ea0e8 100644 --- a/src/Policies/PolicyTrait.php +++ b/src/Policies/PolicyTrait.php @@ -60,7 +60,7 @@ public function setToken(PersonalAccessToken $token = null): self public function verify(Authenticatable $user, string $ability): bool|Response { - $verify = config('playground.auth.verify'); + $verify = config('playground-auth.verify'); // dd([ // '__METHOD__' => __METHOD__, // '$verify' => $verify, @@ -75,10 +75,15 @@ public function verify(Authenticatable $user, string $ability): bool|Response // A user with an email address passes. return ! empty($user->getAttribute('email')); } - Log::debug(__METHOD__, [ - '$ability' => $ability, - '$user' => $user, - ]); + + if (config('app.debug') && config('playground-auth.debug')) { + Log::debug(__METHOD__, [ + 'error' => 'Unexpected verify security mechanism for playground-auth.verify or PLAYGROUND_AUTH_VERIFY. Options: privileges|roles|user', + '$ability' => $ability, + '$verify' => $verify, + '$user' => $user->toArray(), + ]); + } return false; } diff --git a/src/Policies/PrivilegeTrait.php b/src/Policies/PrivilegeTrait.php index e2f40f4..b17936c 100644 --- a/src/Policies/PrivilegeTrait.php +++ b/src/Policies/PrivilegeTrait.php @@ -67,36 +67,36 @@ private function hasPrivilegeWildcard(string $privilege): bool public function hasPrivilege(Authenticatable $user, string $privilege): bool|Response { if (empty($privilege)) { - return Response::denyWithStatus(406, __('playground::auth.unacceptable')); + return Response::denyWithStatus(406, __('playground-auth::auth.unacceptable')); } - if (config('playground.auth.sanctum')) { + if (config('playground-auth.sanctum')) { if ($user instanceof HasApiTokens) { return $this->hasPrivilegeSanctum($user, $privilege); } else { - return Response::denyWithStatus(401, __('playground::auth.unauthorized')); + return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } } - if (config('playground.auth.hasPrivilege') && method_exists($user, 'hasPrivilege')) { + if (config('playground-auth.hasPrivilege') && method_exists($user, 'hasPrivilege')) { return $user->hasPrivilege($privilege); } - if (config('playground.auth.userPrivileges') && array_key_exists('privileges', $user->getAttributes())) { + if (config('playground-auth.userPrivileges') && array_key_exists('privileges', $user->getAttributes())) { $privileges = $user->getAttribute('privileges'); if (is_array($privileges) && in_array($privilege, $privileges)) { return true; } } - return Response::denyWithStatus(401, __('playground::auth.unauthorized')); + return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): bool|Response { if (empty($privilege)) { - return Response::denyWithStatus(406, __('playground::auth.unacceptable')); + return Response::denyWithStatus(406, __('playground-auth::auth.unacceptable')); } if (! $this->hasToken()) { @@ -104,7 +104,7 @@ private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): boo * @var PersonalAccessToken $token */ $token = $user->tokens() - ->where('name', config('playground.auth.token.name')) + ->where('name', config('playground-auth.token.name')) // Get the latest created token. ->orderBy('created_at', 'desc') ->first(); @@ -113,7 +113,7 @@ private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): boo $this->setToken($token); $user->withAccessToken($token); } else { - return Response::denyWithStatus(401, __('playground::auth.unauthorized')); + return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } } @@ -124,7 +124,7 @@ private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): boo $token = $this->getToken(); if (! $token || $token->cant($privilege)) { - return Response::denyWithStatus(401, __('playground::auth.unauthorized')); + return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } // dd([ diff --git a/src/Policies/RoleTrait.php b/src/Policies/RoleTrait.php index a17814f..459207f 100644 --- a/src/Policies/RoleTrait.php +++ b/src/Policies/RoleTrait.php @@ -63,7 +63,7 @@ public function isRoot(Authenticatable $user): bool { $isRoot = false; - if (! empty(config('playground.auth.userRole'))) { + if (! empty(config('playground-auth.userRole'))) { $isRoot = $user->getAttributeValue('role') === 'root'; } @@ -109,10 +109,10 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } else { $roles = $this->getRolesForAdmin(); // // Invalid role - // return Response::denyWithStatus(406, __('playground::auth.unacceptable')); + // return Response::denyWithStatus(406, __('playground-auth::auth.unacceptable')); } - if (config('playground.auth.hasRole') && method_exists($user, 'hasRole')) { + if (config('playground-auth.hasRole') && method_exists($user, 'hasRole')) { // Check for any role. foreach ($roles as $role) { if ($user->hasRole($role)) { @@ -121,7 +121,7 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } } - if (config('playground.auth.userRole')) { + if (config('playground-auth.userRole')) { // Check for any role. foreach ($roles as $role) { if (! empty($user->role) && $role === $user->role) { @@ -130,7 +130,7 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } } - if (config('playground.auth.userRoles')) { + if (config('playground-auth.userRoles')) { if (is_array($roles) && ! empty($user->roles) && is_array($user->roles) @@ -143,16 +143,15 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } } - // dd([ - // '__METHOD__' => __METHOD__, + // dump([ // '__METHOD__' => __METHOD__, // '$user' => $user, - // 'userRole' => config('playground.auth.userRole'), - // 'userRoles' => config('playground.auth.userRoles'), - // 'hasRole' => config('playground.auth.hasRole') && method_exists($user, 'hasRole'), + // 'userRole' => config('playground-auth.userRole'), + // 'userRoles' => config('playground-auth.userRoles'), + // 'hasRole' => config('playground-auth.hasRole') && method_exists($user, 'hasRole'), // '$ability' => $ability, // '$roles' => $roles, // ]); - return Response::denyWithStatus(401, __('playground::auth.unauthorized')); + return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 079021d..19d4137 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -139,6 +139,8 @@ public function about(): void 'Load Commands' => ! empty($load['commands']) ? 'ENABLED' : 'DISABLED', 'Load Translations' => ! empty($load['translations']) ? 'ENABLED' : 'DISABLED', + 'Debug Mode' => ! empty($config['debug']) && ! empty(config('app.debug')) ? 'ENABLED' : 'OFF', + 'Token [Abilities]' => sprintf('[%s]', $token['abilities']), 'Token [Expires]' => sprintf('[%s]', $token['expires']), 'Token [Name]' => sprintf('[%s]', $token['name']), diff --git a/tests/Unit/Policies/ModelPolicy/AbstractRoleTest.php b/tests/Unit/Policies/ModelPolicy/AbstractRoleTest.php index bb3531f..07b476d 100644 --- a/tests/Unit/Policies/ModelPolicy/AbstractRoleTest.php +++ b/tests/Unit/Policies/ModelPolicy/AbstractRoleTest.php @@ -21,9 +21,9 @@ protected function setUp(): void parent::setUp(); config([ - 'playground.auth.verify' => 'roles', - 'playground.auth.hasRole' => true, - 'playground.auth.userRoles' => true, + 'playground-auth.verify' => 'roles', + 'playground-auth.hasRole' => true, + 'playground-auth.userRoles' => true, ]); } diff --git a/tests/Unit/Policies/Policy/AbstractTest.php b/tests/Unit/Policies/Policy/AbstractTest.php index 79814c8..9068634 100644 --- a/tests/Unit/Policies/Policy/AbstractTest.php +++ b/tests/Unit/Policies/Policy/AbstractTest.php @@ -21,9 +21,9 @@ protected function setUp(): void parent::setUp(); config([ - 'playground.auth.userRole' => true, - 'playground.auth.userRoles' => true, - 'playground.auth.verify' => 'roles', + 'playground-auth.userRole' => true, + 'playground-auth.userRoles' => true, + 'playground-auth.verify' => 'roles', ]); } diff --git a/tests/Unit/Policies/PolicyTrait/TraitTest.php b/tests/Unit/Policies/PolicyTrait/TraitTest.php index 89bceac..60d1b45 100644 --- a/tests/Unit/Policies/PolicyTrait/TraitTest.php +++ b/tests/Unit/Policies/PolicyTrait/TraitTest.php @@ -45,8 +45,67 @@ public function test_setToken(): void $this->assertIsObject($instance->setToken()); } - public function test_verify(): void + public function test_verify_does_not_log_when_app_debugging_is_disabled(): void { + config([ + 'app.debug' => false, + 'playground-auth.debug' => true, + ]); + + $instance = new Policy; + + $log = LogFake::bind(); + + /** + * @var User $user + */ + $user = User::factory()->make(); + + $verify = 'invalid-verifier'; + + config(['playground-auth.verify' => $verify]); + + $ability = 'view'; + + $this->assertFalse($instance->verify($user, $ability)); + + $log->assertNothingLogged(); + } + + public function test_verify_does_not_log_when_auth_debugging_is_disabled(): void + { + config([ + 'app.debug' => true, + 'playground-auth.debug' => false, + ]); + + $instance = new Policy; + + $log = LogFake::bind(); + + /** + * @var User $user + */ + $user = User::factory()->make(); + + $verify = 'invalid-verifier'; + + config(['playground-auth.verify' => $verify]); + + $ability = 'view'; + + $this->assertFalse($instance->verify($user, $ability)); + + $log->assertNothingLogged(); + } + + public function test_verify_logs_with_debugging_enabled(): void + { + config([ + 'app.debug' => true, + 'playground-auth.debug' => true, + ]); + $instance = new Policy; $log = LogFake::bind(); @@ -58,7 +117,7 @@ public function test_verify(): void $verify = 'invalid-verifier'; - config(['playground.auth.verify' => $verify]); + config(['playground-auth.verify' => $verify]); $ability = 'view'; @@ -87,7 +146,7 @@ public function test_verify_privileges(): void $verify = 'privileges'; - config(['playground.auth.verify' => $verify]); + config(['playground-auth.verify' => $verify]); $ability = 'view'; @@ -108,7 +167,7 @@ public function test_verify_roles(): void $verify = 'roles'; - config(['playground.auth.verify' => $verify]); + config(['playground-auth.verify' => $verify]); $ability = 'view'; @@ -129,7 +188,7 @@ public function test_verify_user(): void $verify = 'user'; - config(['playground.auth.verify' => $verify]); + config(['playground-auth.verify' => $verify]); $ability = 'view'; diff --git a/tests/Unit/Policies/PrivilegeTrait/TraitTest.php b/tests/Unit/Policies/PrivilegeTrait/TraitTest.php index 1c74524..740ffeb 100644 --- a/tests/Unit/Policies/PrivilegeTrait/TraitTest.php +++ b/tests/Unit/Policies/PrivilegeTrait/TraitTest.php @@ -67,7 +67,7 @@ public function test_hasPrivilege(): void { $instance = new PrivilegeModelPolicy; - config(['playground.auth.sanctum' => true]); + config(['playground-auth.sanctum' => true]); /** * @var UserWithSanctum $user @@ -86,7 +86,10 @@ public function test_hasPrivilege_with_user_hasPrivilege(): void { $instance = new PrivilegeModelPolicy; - config(['playground.auth.hasPrivilege' => true]); + config([ + 'playground-auth.hasPrivilege' => true, + 'playground-auth.sanctum' => false, + ]); /** * @var UserWithRoleAndRolesAndPrivileges $user @@ -106,7 +109,10 @@ public function test_hasPrivilege_with_user_privileges(): void { $instance = new PrivilegeModelPolicy; - config(['playground.auth.userPrivileges' => true]); + config([ + 'playground-auth.userPrivileges' => true, + 'playground-auth.sanctum' => false, + ]); /** * @var UserWithRoleAndRolesAndPrivileges $user @@ -127,8 +133,9 @@ public function test_hasPrivilege_without_privileges_enabled(): void $instance = new PrivilegeModelPolicy; config([ - 'playground.auth.hasPrivilege' => false, - 'playground.auth.userPrivileges' => false, + 'playground-auth.hasPrivilege' => false, + 'playground-auth.userPrivileges' => false, + 'playground-auth.sanctum' => false, ]); /** diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index ad0f064..63d1f22 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -30,7 +30,7 @@ protected function getEnvironmentSetUp($app) { // dd(__METHOD__); $app['config']->set('auth.providers.users.model', 'Playground\\Test\\Models\\User'); - $app['config']->set('playground.auth.verify', 'user'); + $app['config']->set('playground-auth.verify', 'user'); // $app['config']->set('playground-auth.redirect', true); // $app['config']->set('playground-auth.session', true);