From 4ded607398d804e16c16e96c5a149e4e3c3cab50 Mon Sep 17 00:00:00 2001 From: Jeremy Postlethwaite Date: Sat, 10 Feb 2024 02:13:11 -0800 Subject: [PATCH] GH-7 --- config/playground-auth.php | 10 +- lang/en/auth.php | 1 + src/Issuer.php | 225 +++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 src/Issuer.php diff --git a/config/playground-auth.php b/config/playground-auth.php index 741e9ec..0b27e4a 100644 --- a/config/playground-auth.php +++ b/config/playground-auth.php @@ -4,10 +4,14 @@ 'redirect' => env('PLAYGROUND_AUTH_REDIRECT', null), // 'session' => false, 'token' => [ - // 'expires' => 60 * 24, - 'expires' => 'tomorrow midnight', + // 'abilities' => '', + // 'abilities' => 'user', + 'abilities' => 'merge', + // 'expires' => 'tomorrow midnight', + 'expires' => null, 'name' => 'app', // @see playground.auth.token.name + 'listed' => false, 'roles' => false, 'privileges' => true, 'sanctum' => true, @@ -29,7 +33,7 @@ 'managers' => [ // 'manager@example.com', ], - 'privileges' => [ + 'abilities' => [ 'root' => [ '*', 'app:*', diff --git a/lang/en/auth.php b/lang/en/auth.php index d4c7641..c97ab61 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -18,4 +18,5 @@ 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 'required' => 'Authentication is required. Please log in.', + 'sanctum.disabled' => 'Sorry, Sanctum authenticaction is currently disabled.', ]; diff --git a/src/Issuer.php b/src/Issuer.php new file mode 100644 index 0000000..4dac414 --- /dev/null +++ b/src/Issuer.php @@ -0,0 +1,225 @@ + + */ + protected array $abilities = []; + + protected bool $isRoot = false; + + protected bool $isAdmin = false; + + protected bool $isManager = false; + + protected bool $isUser = false; + + protected bool $isGuest = false; + + protected bool $hasAbilities = false; + + protected bool $hasPrivileges = false; + + protected bool $hasRoles = false; + + protected bool $hasSanctum = false; + + protected bool $useSanctum = false; + + protected bool $onlyUserAbilities = false; + + /** + * @return array + */ + protected function abilitiesByGroup(string $group): array + { + $abilities = config('playground-auth.abilities.'.$group); + + return is_array($abilities) ? $abilities : []; + } + + /** + * TODO This should work with any kind of authentication system. Identify what is supported. + * + * Types: + * - User::$priviliges + * - User::hasPrivilige() + * - User::$roles + * - User::hasRole() - with string or array? + * - User::hasRoles() + * - Auth::user()?->currentAccessToken()?->can('app:*') + * - Auth::user()?->currentAccessToken()?->can($withPrivilege.':create') + * + * @experimental Subject to change + * + * @return array + */ + protected function abilities(Authenticatable $user): array + { + $abilities = []; + + if ($this->onlyUserAbilities) { + $abilities = []; + } elseif ($this->isRoot) { + $abilities = $this->abilitiesByGroup('root'); + } elseif ($this->isAdmin) { + $abilities = $this->abilitiesByGroup('admin'); + } elseif ($this->isManager) { + $abilities = $this->abilitiesByGroup('manager'); + } elseif ($this->isUser) { + $abilities = $this->abilitiesByGroup('user'); + } elseif ($this->isGuest) { + $abilities = $this->abilitiesByGroup('guest'); + } + + foreach ($abilities as $ability) { + if (is_string($ability) + && $ability + && ! in_array($ability, $this->abilities) + ) { + $this->abilities[] = $ability; + } + } + + if (empty($this->abilities)) { + $this->abilities[] = 'none'; + } + + return $this->abilities; + } + + /** + * @param array $config + */ + public function init(Authenticatable $user, array $config): void + { + if ($user instanceof HasApiTokens) { + $this->hasSanctum = ! empty($config['sanctum']); + } else { + $this->hasSanctum = false; + } + + if ($user instanceof Abilities) { + if (empty($config['abilities'])) { + $this->abilities = []; + $this->onlyUserAbilities = false; + } else { + $abilities = $user->getAttributeValue('abilities'); + $this->abilities = is_array($abilities) ? $abilities : []; + $this->onlyUserAbilities = $config['abilities'] === 'user'; + } + } else { + $this->abilities = []; + $this->onlyUserAbilities = false; + } + + if ($user instanceof Admin) { + $this->isAdmin = $user->isAdmin(); + } else { + $this->isAdmin = false; + } + + if ($user instanceof Privileges) { + $this->hasPrivileges = ! empty($config['privileges']); + } else { + $this->hasPrivileges = false; + } + + if ($user instanceof Role) { + $this->hasRoles = ! empty($config['roles']); + if ($this->hasRoles) { + $this->isRoot = $user->hasRole('root'); + if (! $this->isAdmin) { + $this->isAdmin = $user->hasRole('admin'); + } + $this->isUser = $user->hasRole('user'); + $this->isGuest = ! ($this->isRoot || $this->isAdmin || $this->isManager || $this->isUser); + } + } else { + $this->hasRoles = false; + $this->isRoot = false; + $this->isUser = false; + $this->isGuest = false; + } + + if (! empty($config['listed'])) { + $this->listed($user); + } + } + + public function listed(Authenticatable $user): void + { + $email = $user->getAttributeValue('email'); + $managers = config('playground-auth.managers'); + if (is_array($managers)) { + if ($email && in_array($email, $managers)) { + $this->isManager = true; + } + } + + $admins = config('playground-auth.admins'); + if (is_array($admins)) { + if ($email && in_array($email, $admins)) { + $this->isAdmin = true; + } + } + } + + /** + * @param Authenticatable&HasApiTokens $user + * @return array + */ + public function sanctum(HasApiTokens $user): array + { + /** + * @var array $config + */ + $config = config('playground-auth.token'); + + $this->init($user, $config); + + if (! $this->hasSanctum) { + throw new \Exception(__('playground-auth:auth.sanctum.disabled')); + } + + $tokens = []; + + $name = 'app'; + if (! empty($config['name']) && is_string($config['name'])) { + $name = $config['name']; + } + + // $expiresAt = null; + // if (! empty($config['expires']) && is_string($config['expires'])) { + // $expiresAt = new Carbon($config['expires']); + // } + + // dd([ + // '__METHOD__' => __METHOD__, + // 'createToken' => $user->createToken($name, $abilities, $expiresAt)->toArray(), + // ]); + $tokens[$name] = $user->createToken( + $name, + $this->abilities($user) + )->plainTextToken; + + return $tokens; + } +}