124 lines
3.9 KiB
PHP
124 lines
3.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Permission Resolver — Core of the Role-Based Permission System
|
|
*
|
|
* Resolution order (first match wins):
|
|
* 1. Server-level explicit DENY → false
|
|
* 2. Server-level explicit ALLOW → true
|
|
* 3. Role DENY (highest priority role first) → false
|
|
* 4. Role ALLOW → true
|
|
* 5. Default → false
|
|
*
|
|
* @author Ball Studios <https://git.balls.studio>
|
|
* @license MIT
|
|
*/
|
|
|
|
namespace Pterodactyl\Addons\AdvancedAdmin\Services\Roles;
|
|
|
|
use Illuminate\Support\Collection;
|
|
use Pterodactyl\Addons\AdvancedAdmin\Models\Role;
|
|
use Pterodactyl\Addons\AdvancedAdmin\Models\RoleAuditLog;
|
|
use Pterodactyl\Addons\AdvancedAdmin\Models\ServerPermissionOverride;
|
|
use Pterodactyl\Addons\AdvancedAdmin\Models\ServerRoleAssignment;
|
|
|
|
class PermissionResolver
|
|
{
|
|
/**
|
|
* Resolve whether a user has a permission on a server.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function resolve(int $userId, int $serverId, string $permission): bool
|
|
{
|
|
// 1 & 2: Check server-level override
|
|
$override = ServerPermissionOverride::query()
|
|
->where('server_id', $serverId)
|
|
->where('user_id', $userId)
|
|
->where('permission', $permission)
|
|
->first();
|
|
|
|
if ($override && !$override->isUnset()) {
|
|
return $override->isAllow();
|
|
}
|
|
|
|
// 3 & 4: Check role-level (sorted by priority desc — highest wins)
|
|
$roles = $this->getUserRolesForServer($userId, $serverId);
|
|
|
|
foreach ($roles as $role) {
|
|
$value = $role->getPermission($permission);
|
|
if ($value === 'deny') return false;
|
|
if ($value === 'allow') return true;
|
|
}
|
|
|
|
// 5: Default deny
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get full effective permission map for a user on a server.
|
|
* Returns array of [permission => ['value' => bool, 'source' => string]]
|
|
*
|
|
* @param string[] $allPermissions
|
|
* @return array<string, array{value: bool, source: string}>
|
|
*/
|
|
public function resolveAll(int $userId, int $serverId, array $allPermissions): array
|
|
{
|
|
$overrides = ServerPermissionOverride::query()
|
|
->where('server_id', $serverId)
|
|
->where('user_id', $userId)
|
|
->get()
|
|
->keyBy('permission');
|
|
|
|
$roles = $this->getUserRolesForServer($userId, $serverId);
|
|
|
|
$result = [];
|
|
foreach ($allPermissions as $permission) {
|
|
[$value, $source] = $this->resolveWithSource($permission, $overrides, $roles);
|
|
$result[$permission] = ['value' => $value, 'source' => $source];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get user's assigned roles for a server, ordered by priority DESC.
|
|
*/
|
|
public function getUserRolesForServer(int $userId, int $serverId): Collection
|
|
{
|
|
return ServerRoleAssignment::query()
|
|
->where('server_id', $serverId)
|
|
->where('user_id', $userId)
|
|
->with(['role.permissions'])
|
|
->get()
|
|
->map(fn ($a) => $a->role)
|
|
->filter()
|
|
->sortByDesc('priority')
|
|
->values();
|
|
}
|
|
|
|
// ─── Internal ─────────────────────────────────────────────────────────────
|
|
|
|
private function resolveWithSource(
|
|
string $permission,
|
|
Collection $overrides,
|
|
Collection $roles
|
|
): array {
|
|
// Server override
|
|
if ($overrides->has($permission)) {
|
|
$o = $overrides->get($permission);
|
|
if (!$o->isUnset()) {
|
|
return [$o->isAllow(), 'server_override'];
|
|
}
|
|
}
|
|
|
|
// Role
|
|
foreach ($roles as $role) {
|
|
$value = $role->getPermission($permission);
|
|
if ($value === 'deny') return [false, "role:{$role->name}"];
|
|
if ($value === 'allow') return [true, "role:{$role->name}"];
|
|
}
|
|
|
|
return [false, 'default'];
|
|
}
|
|
}
|