permissions refactoring
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
enum Permission: string
|
||||
{
|
||||
case GlobalAdmin = 'global:admin';
|
||||
case ProjectCreate = 'project:create';
|
||||
case ProjectRead = 'project:read';
|
||||
case ProjectWrite = 'project:write';
|
||||
case ProjectAdmin = 'project:admin';
|
||||
case TeamInvite = 'team:invite';
|
||||
case TeamManage = 'team:manage';
|
||||
case TeamAdmin = 'team:admin';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::GlobalAdmin => 'Global admin',
|
||||
self::ProjectCreate => 'Project create',
|
||||
self::ProjectRead => 'Project read',
|
||||
self::ProjectWrite => 'Project write',
|
||||
self::ProjectAdmin => 'Project admin',
|
||||
self::TeamInvite => 'Team invite',
|
||||
self::TeamManage => 'Team manage',
|
||||
self::TeamAdmin => 'Team admin',
|
||||
};
|
||||
}
|
||||
|
||||
public function scope(): PermissionScope
|
||||
{
|
||||
return match ($this) {
|
||||
self::GlobalAdmin, self::ProjectCreate => PermissionScope::Global,
|
||||
self::ProjectRead, self::ProjectWrite, self::ProjectAdmin => PermissionScope::Project,
|
||||
self::TeamInvite, self::TeamManage, self::TeamAdmin => PermissionScope::Team,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, self>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
foreach ($this->impliedPermissions() as $impliedPermission) {
|
||||
if ($impliedPermission->includes($permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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<int, self>|null $permissions
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function values(?array $permissions = null): array
|
||||
{
|
||||
return array_map(
|
||||
fn (self $permission): string => $permission->value,
|
||||
$permissions ?? self::cases(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, self>
|
||||
*/
|
||||
public static function globalAssignable(): array
|
||||
{
|
||||
return [
|
||||
self::GlobalAdmin,
|
||||
self::ProjectCreate,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, self>
|
||||
*/
|
||||
public static function projectAssignable(): array
|
||||
{
|
||||
return [
|
||||
self::ProjectRead,
|
||||
self::ProjectWrite,
|
||||
self::ProjectAdmin,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, self>
|
||||
*/
|
||||
public static function teamAssignable(): array
|
||||
{
|
||||
return [
|
||||
self::TeamInvite,
|
||||
self::TeamManage,
|
||||
self::TeamAdmin,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, self>
|
||||
*/
|
||||
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<int, self> $permissions
|
||||
* @return array<int, array{label: string, value: string}>
|
||||
*/
|
||||
public static function options(array $permissions): array
|
||||
{
|
||||
return array_map(
|
||||
fn (self $permission): array => [
|
||||
'label' => $permission->label(),
|
||||
'value' => $permission->value,
|
||||
],
|
||||
$permissions,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user