Files
KanbanGit/app/Enums/Permission.php
T
2026-04-12 19:21:04 +02:00

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,
);
}
}