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