'Global admin', self::ProjectCreate => 'Project create', self::ProjectRead => 'Project read', self::ProjectWrite => 'Project write', self::ProjectAdmin => 'Project admin', self::TeamCreate => 'Team create', self::TeamInvite => 'Team invite', self::TeamManage => 'Team manage', self::TeamAdmin => 'Team admin', }; } public function scope(): PermissionScope { return match ($this) { self::GlobalAdmin, self::ProjectCreate, self::TeamCreate => PermissionScope::Global, self::ProjectRead, self::ProjectWrite, self::ProjectAdmin => PermissionScope::Project, self::TeamInvite, self::TeamManage, self::TeamAdmin => PermissionScope::Team, }; } /** * @return array */ public function impliedPermissions(): array { return match ($this) { self::GlobalAdmin => array_values(array_filter( self::cases(), fn (self $permission): bool => $permission !== self::GlobalAdmin, )), self::ProjectAdmin => [self::ProjectWrite, self::ProjectRead], self::ProjectWrite => [self::ProjectRead], self::TeamAdmin => [self::TeamManage, self::TeamInvite], default => [], }; } public function includes(self $permission): bool { if ($this === $permission) { return true; } return array_any($this->impliedPermissions(), fn ($impliedPermission) => $impliedPermission->includes($permission)); } public function isAssignableTo(Model|string $assignee): bool { $assigneeClass = $assignee instanceof Model ? $assignee::class : $assignee; return match ($this->scope()) { PermissionScope::Global => is_a($assigneeClass, User::class, true), PermissionScope::Project => is_a($assigneeClass, User::class, true) || is_a($assigneeClass, Team::class, true), PermissionScope::Team => is_a($assigneeClass, User::class, true), }; } public function matchesScope(Model|string|null $scope): bool { $scopeClass = $scope instanceof Model ? $scope::class : $scope; return match ($this->scope()) { PermissionScope::Global => $scopeClass === null, PermissionScope::Project => is_a($scopeClass, Project::class, true), PermissionScope::Team => is_a($scopeClass, Team::class, true), }; } /** * @param array|null $permissions * @return array */ public static function values(?array $permissions = null): array { return array_map( fn (self $permission): string => $permission->value, $permissions ?? self::cases(), ); } /** * @return array */ public static function globalAssignable(): array { return [ self::GlobalAdmin, self::TeamCreate, self::ProjectCreate, ]; } /** * @return array */ public static function projectAssignable(): array { return [ self::ProjectRead, self::ProjectWrite, self::ProjectAdmin, ]; } /** * @return array */ public static function teamAssignable(): array { return [ self::TeamInvite, self::TeamManage, self::TeamAdmin, ]; } /** * @return array */ public static function assignableFor(Model|string $assignee, Model|string|null $scope): array { return array_values(array_filter( self::cases(), fn (self $permission): bool => $permission->isAssignableTo($assignee) && $permission->matchesScope($scope), )); } /** * @param array $permissions * @return array */ public static function options(array $permissions): array { return array_map( fn (self $permission): array => [ 'label' => $permission->label(), 'value' => $permission->value, ], $permissions, ); } }