176 lines
4.9 KiB
PHP
176 lines
4.9 KiB
PHP
<?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,
|
|
);
|
|
}
|
|
}
|