diff --git a/config/playground-auth.php b/config/playground-auth.php index 877c025..1581050 100644 --- a/config/playground-auth.php +++ b/config/playground-auth.php @@ -184,6 +184,11 @@ * @var string session_name The session name for the token. */ 'session_name' => env('PLAYGROUND_AUTH_TOKEN_SESSION_NAME', 'sanctum'), + + /** + * @var bool transient Allow transient tokens in policies. + */ + 'transient' => (bool) env('PLAYGROUND_AUTH_TOKEN_TRANSIENT', false), ], /* diff --git a/src/Issuer.php b/src/Issuer.php index 5deb37b..6b923d4 100644 --- a/src/Issuer.php +++ b/src/Issuer.php @@ -186,19 +186,6 @@ protected function compileAbilitiesByGroup(string $group): array } } - // 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; } @@ -360,11 +347,6 @@ public function authorize(Authenticatable $user): array } else { $tokens = []; } - // dd([ - // '$tokens' => $tokens, - // '$this->useSanctum' => $this->useSanctum, - // '$this->hasSanctum' => $this->hasSanctum, - // ]); return $tokens; } @@ -378,9 +360,6 @@ public function sanctum(Authenticatable $user): array * @var array $config */ $config = config('playground-auth.token'); - // dump([ - // '$config' => $config, - // ]); $this->init($user); @@ -406,8 +385,8 @@ public function sanctum(Authenticatable $user): array if (is_callable([$user, 'createToken'])) { $tokens[$name] = $user->createToken( $name, - $this->abilities($user) - // $expiresAt + $this->abilities($user), + $expiresAt )->plainTextToken; } diff --git a/src/Policies/Policy.php b/src/Policies/Policy.php index 40ec883..27bfa16 100644 --- a/src/Policies/Policy.php +++ b/src/Policies/Policy.php @@ -85,6 +85,7 @@ public function index(Authenticatable $user): bool|Response // '$this->allowRootOverride' => $this->allowRootOverride, // '$this->package' => $this->package, // '$this->entity' => $this->entity, + // 'config(playground-auth)' => config('playground-auth'), // ]); // \Log::debug(__METHOD__, [ diff --git a/src/Policies/PolicyTrait.php b/src/Policies/PolicyTrait.php index 84dcb7f..df927f4 100644 --- a/src/Policies/PolicyTrait.php +++ b/src/Policies/PolicyTrait.php @@ -63,12 +63,6 @@ public function setToken(PersonalAccessToken $token = null): self public function verify(Authenticatable $user, string $ability): bool|Response { $verify = config('playground-auth.verify'); - // dd([ - // '__METHOD__' => __METHOD__, - // '$verify' => $verify, - // '$ability' => $ability, - // '$user' => $user, - // ]); if (in_array($verify, ['policy', 'privileges', 'sanctum'])) { return $this->hasPrivilege($user, $this->privilege($ability)); } elseif ($verify === 'roles') { diff --git a/src/Policies/PrivilegeTrait.php b/src/Policies/PrivilegeTrait.php index 753674a..1cb210a 100644 --- a/src/Policies/PrivilegeTrait.php +++ b/src/Policies/PrivilegeTrait.php @@ -8,8 +8,10 @@ use Illuminate\Auth\Access\Response; use Illuminate\Contracts\Auth\Authenticatable; +use Illuminate\Support\Facades\Log; use Laravel\Sanctum\Contracts\HasApiTokens; use Laravel\Sanctum\PersonalAccessToken; +use Laravel\Sanctum\TransientToken; /** * \Playground\Auth\Policies\PrivilegeTrait @@ -52,13 +54,17 @@ public function privilege(string $ability = '*'): string private function hasPrivilegeWildcard(string $privilege): bool { + $token = $this->getToken(); + if (! $token) { + return false; + } $check = ''; foreach (explode(':', $privilege) as $part) { if ($check) { $check .= ':'; } $check .= $part; - if ($this->getToken()?->can($check.':*')) { + if ($token->can($check.':*')) { return true; } } @@ -74,7 +80,7 @@ public function hasPrivilege(Authenticatable $user, string $privilege): bool|Res if (config('playground-auth.sanctum')) { - if ($user instanceof HasApiTokens) { + if ($user instanceof HasApiTokens || is_callable([$user, 'tokenCan'])) { return $this->hasPrivilegeSanctum($user, $privilege); } else { return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); @@ -95,26 +101,48 @@ public function hasPrivilege(Authenticatable $user, string $privilege): bool|Res return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } - private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): bool|Response + private function hasPrivilegeSanctum(Authenticatable $user, string $privilege): bool|Response { + if (! ($user instanceof HasApiTokens || is_callable([$user, 'tokenCan']))) { + Log::error('The user model does not support Sanctum. Use HasApiTokens.', [ + 'user' => $user, + 'privilege' => $privilege, + 'config(playground-auth)' => config('playground-auth'), + ]); + + return Response::denyWithStatus(500, __('playground-auth::auth.sanctum.disabled')); + } + if (empty($privilege)) { return Response::denyWithStatus(406, __('playground-auth::auth.unacceptable')); } if (! $this->hasToken()) { + /** - * @var PersonalAccessToken $token + * @var ?PersonalAccessToken $token */ - $token = $user->tokens() - ->where('name', config('playground-auth.token.name')) - // Get the latest created token. - ->orderBy('created_at', 'desc') - ->first(); - - if ($token) { - $this->setToken($token); - $user->withAccessToken($token); - } else { + $token = method_exists($user, 'currentAccessToken') ? $user->currentAccessToken() : null; + if ($token instanceof TransientToken) { + if (empty(config('playground-auth.token.transient'))) { + $token = null; + } + } + + if (! $token && method_exists($user, 'tokens')) { + $token = $user->tokens() + ->where('name', config('playground-auth.token.name')) + // Get the latest created token. + ->orderBy('created_at', 'desc') + ->first(); + + if ($token && method_exists($user, 'withAccessToken')) { + $this->setToken($token); + $user->withAccessToken($token); + } + } + + if (! $token) { return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } } @@ -129,13 +157,6 @@ private function hasPrivilegeSanctum(HasApiTokens $user, string $privilege): boo return Response::denyWithStatus(401, __('playground-auth::auth.unauthorized')); } - // dd([ - // '__METHOD__' => __METHOD__, - // '$privilege' => $privilege, - // '$user->tokens()->first()' => $user->tokens()->where('name', config('playground-auth.token.name'))->first()->can($privilege), - // '$user->currentAccessToken()->can($privilege)' => $user->currentAccessToken()->can($privilege), - // '$user->currentAccessToken()->cant($privilege)' => $user->currentAccessToken()->cant($privilege), - // ]); return true; } } diff --git a/src/Policies/RoleTrait.php b/src/Policies/RoleTrait.php index 83f9bbf..aa49aa1 100644 --- a/src/Policies/RoleTrait.php +++ b/src/Policies/RoleTrait.php @@ -145,15 +145,6 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } } - // dump([ - // '__METHOD__' => __METHOD__, - // '$user' => $user, - // '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::auth.unauthorized')); } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9acffbc..c89ebc2 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -147,6 +147,8 @@ public function about(): void 'Debug Mode' => ! empty($config['debug']) && ! empty(config('app.debug')) ? 'ENABLED' : 'OFF', + 'Verify' => ! empty($config['verify']) && is_string($config['verify']) ? sprintf('[%s]', $config['verify']) : '', + 'Token [Abilities]' => sprintf('[%s]', $token['abilities']), 'Token [Expires]' => sprintf('[%s]', $token['expires']), 'Token [Name]' => sprintf('[%s]', $token['name']), diff --git a/tests/Unit/Policies/PolicyTrait/TraitTest.php b/tests/Unit/Policies/PolicyTrait/TraitTest.php index 7a47dfa..ee0dcf6 100644 --- a/tests/Unit/Policies/PolicyTrait/TraitTest.php +++ b/tests/Unit/Policies/PolicyTrait/TraitTest.php @@ -9,9 +9,8 @@ use Illuminate\Auth\Access\Response; use Playground\Test\Models\User; use Tests\Unit\Playground\Auth\TestCase; - -// use TiMacDonald\Log\LogEntry; -// use TiMacDonald\Log\LogFake; +use TiMacDonald\Log\LogEntry; +use TiMacDonald\Log\LogFake; /** * \Tests\Unit\Playground\Auth\Policies\PolicyTrait\TraitTest @@ -57,7 +56,7 @@ public function test_verify_does_not_log_when_app_debugging_is_disabled(): void $instance = new Policy; - // $log = LogFake::bind(); + $log = LogFake::bind(); /** * @var User $user @@ -72,7 +71,7 @@ public function test_verify_does_not_log_when_app_debugging_is_disabled(): void $this->assertFalse($instance->verify($user, $ability)); - // $log->assertNothingLogged(); + $log->assertNothingLogged(); } public function test_verify_does_not_log_when_auth_debugging_is_disabled(): void @@ -84,7 +83,7 @@ public function test_verify_does_not_log_when_auth_debugging_is_disabled(): void $instance = new Policy; - // $log = LogFake::bind(); + $log = LogFake::bind(); /** * @var User $user @@ -99,7 +98,7 @@ public function test_verify_does_not_log_when_auth_debugging_is_disabled(): void $this->assertFalse($instance->verify($user, $ability)); - // $log->assertNothingLogged(); + $log->assertNothingLogged(); } public function test_verify_logs_with_debugging_enabled(): void @@ -111,7 +110,7 @@ public function test_verify_logs_with_debugging_enabled(): void $instance = new Policy; - // $log = LogFake::bind(); + $log = LogFake::bind(); /** * @var User $user @@ -126,16 +125,16 @@ public function test_verify_logs_with_debugging_enabled(): void $this->assertFalse($instance->verify($user, $ability)); - // $log->assertLogged( - // fn (LogEntry $log) => $log->level === 'debug' - // ); + $log->assertLogged( + fn (LogEntry $log) => $log->level === 'debug' + ); - // $log->assertLogged( - // fn (LogEntry $log) => str_contains( - // is_string($log->context['$ability']) ? $log->context['$ability'] : '', - // $ability - // ) - // ); + $log->assertLogged( + fn (LogEntry $log) => str_contains( + is_string($log->context['$ability']) ? $log->context['$ability'] : '', + $ability + ) + ); } public function test_verify_privileges(): void