File transfer

This commit is contained in:
2026-06-11 20:33:44 -05:00
parent 2cb1d58264
commit b362357bcb
58 changed files with 5096 additions and 4 deletions

View File

@@ -0,0 +1,155 @@
<?php
/**
* Permission Resolver Unit Tests
*
* @author Ball Studios <https://git.balls.studio>
* @license MIT
*/
namespace Tests\Addons\Unit;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
use Pterodactyl\Addons\AdvancedAdmin\Models\ServerPermissionOverride;
use Pterodactyl\Addons\AdvancedAdmin\Services\Roles\PermissionResolver;
class PermissionResolverTest extends TestCase
{
private PermissionResolver $resolver;
protected function setUp(): void
{
parent::setUp();
$this->resolver = new PermissionResolver();
}
/** Resolution order: server DENY beats role ALLOW */
public function test_server_deny_overrides_role_allow(): void
{
$override = $this->makeOverride('deny');
$role = $this->makeRole('allow');
$result = $this->resolveWithMocks([$override], [$role], 'file.read');
$this->assertFalse($result, 'Server DENY should override role ALLOW');
}
/** Resolution order: server ALLOW beats role DENY */
public function test_server_allow_overrides_role_deny(): void
{
$override = $this->makeOverride('allow');
$role = $this->makeRole('deny');
$result = $this->resolveWithMocks([$override], [$role], 'file.read');
$this->assertTrue($result, 'Server ALLOW should override role DENY');
}
/** Role DENY should deny when no server override is set */
public function test_role_deny_denies_when_no_server_override(): void
{
$override = $this->makeOverride('unset');
$role = $this->makeRole('deny');
$result = $this->resolveWithMocks([$override], [$role], 'file.read');
$this->assertFalse($result, 'Role DENY should deny when server override is unset');
}
/** Role ALLOW should allow when no server override is set */
public function test_role_allow_allows_when_no_server_override(): void
{
$override = $this->makeOverride('unset');
$role = $this->makeRole('allow');
$result = $this->resolveWithMocks([$override], [$role], 'file.read');
$this->assertTrue($result, 'Role ALLOW should allow when server override is unset');
}
/** Default deny when nothing is set */
public function test_default_deny_when_nothing_set(): void
{
$override = $this->makeOverride('unset');
$result = $this->resolveWithMocks([$override], [], 'file.read');
$this->assertFalse($result, 'Should default to deny when no rules set');
}
/** Higher priority role wins on conflict */
public function test_higher_priority_role_wins(): void
{
$lowRole = $this->makeRole('deny', priority: 10);
$highRole = $this->makeRole('allow', priority: 100);
$override = $this->makeOverride('unset');
// High priority role should win (sorted desc by priority)
$result = $this->resolveWithMocks([$override], [$lowRole, $highRole], 'file.read');
$this->assertTrue($result, 'Higher priority role (allow) should win over lower priority (deny)');
}
/** No privilege escalation — deny still blocks even if other role allows */
public function test_explicit_deny_in_any_role_blocks(): void
{
// If ANY role in resolution order says deny first (by priority), it should deny
$denyRole = $this->makeRole('deny', priority: 50);
$allowRole = $this->makeRole('allow', priority: 10);
$override = $this->makeOverride('unset');
$result = $this->resolveWithMocks([$override], [$denyRole, $allowRole], 'file.read');
$this->assertFalse($result, 'Deny in higher-priority role should block allow in lower role');
}
// ─── Helpers ─────────────────────────────────────────────────────────────
private function resolveWithMocks(array $overrides, array $roles, string $permission): bool
{
// Use a test double of the resolver that accepts mock data
$resolver = new class extends PermissionResolver {
public array $mockOverrides = [];
public array $mockRoles = [];
public function resolve(int $userId, int $serverId, string $permission): bool
{
$overridesCollection = collect($this->mockOverrides)->keyBy('permission');
$rolesCollection = collect($this->mockRoles)->sortByDesc('priority')->values();
[$result] = $this->resolveWithSourcePublic($permission, $overridesCollection, $rolesCollection);
return $result;
}
public function resolveWithSourcePublic(string $perm, $overrides, $roles): array
{
return $this->resolveWithSource($perm, $overrides, $roles);
}
};
// This tests the private method via reflection for correctness
$ref = new \ReflectionMethod(PermissionResolver::class, 'resolveWithSource');
$ref->setAccessible(true);
$overridesCol = collect($overrides)->keyBy('permission');
$rolesCol = collect($roles)->sortByDesc('priority')->values();
[$result] = $ref->invoke($this->resolver, $permission, $overridesCol, $rolesCol);
return $result;
}
private function makeOverride(string $value): object
{
return new class ($value) {
public string $permission = 'file.read';
public string $value;
public function __construct(string $v) { $this->value = $v; }
public function isAllow(): bool { return $this->value === 'allow'; }
public function isDeny(): bool { return $this->value === 'deny'; }
public function isUnset(): bool { return $this->value === 'unset'; }
};
}
private function makeRole(string $value, int $priority = 50): object
{
return new class ($value, $priority) {
public int $priority;
public string $name = 'Test Role';
private string $val;
public function __construct(string $v, int $p) { $this->val = $v; $this->priority = $p; }
public function getPermission(string $p): ?string { return $this->val === 'unset' ? null : $this->val; }
};
}
}