Files
2026-06-11 20:33:44 -05:00

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'];
}
}