mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-25 16:26:16 +00:00
Split V5 dashboard behavior into domain controllers and policies, add agent token rotation/revocation, status reconciliation jobs, ingress firewall syncing, and canvas connection APIs. Add migrations for V5 status tracking, server capabilities, resource connection aliases, and revoked agent tokens.
82 lines
2.8 KiB
PHP
82 lines
2.8 KiB
PHP
<?php
|
|
|
|
use App\Services\Flux\AgentTokenIssuer;
|
|
use Firebase\JWT\JWT;
|
|
use Firebase\JWT\Key;
|
|
use Illuminate\Support\Facades\Config;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
beforeEach(function () {
|
|
$this->keyDir = storage_path('framework/testing/flux-keygen-'.bin2hex(random_bytes(4)));
|
|
$this->privateKeyPath = $this->keyDir.'/jwt.priv';
|
|
$this->publicKeyPath = $this->keyDir.'/jwt.pub';
|
|
|
|
Config::set('flux.jwt_private_key_path', $this->privateKeyPath);
|
|
Config::set('flux.jwt_public_key_path', $this->publicKeyPath);
|
|
});
|
|
|
|
afterEach(function () {
|
|
File::deleteDirectory($this->keyDir);
|
|
});
|
|
|
|
/**
|
|
* @return string octal file/dir mode, e.g. "0600"
|
|
*/
|
|
function fluxKeyMode(string $path): string
|
|
{
|
|
clearstatcache();
|
|
|
|
return sprintf('%04o', fileperms($path) & 0777);
|
|
}
|
|
|
|
it('generates a working ES256 keypair at the configured paths with strict perms', function () {
|
|
$this->artisan('v5:flux-generate-keys')->assertSuccessful();
|
|
|
|
expect(File::exists($this->privateKeyPath))->toBeTrue()
|
|
->and(File::exists($this->publicKeyPath))->toBeTrue()
|
|
->and(fluxKeyMode($this->privateKeyPath))->toBe('0600')
|
|
->and(fluxKeyMode($this->publicKeyPath))->toBe('0644')
|
|
->and(fluxKeyMode($this->keyDir))->toBe('0700');
|
|
|
|
// The generated private key must be usable by the real issuer...
|
|
$token = app(AgentTokenIssuer::class)->issue('100.64.0.10');
|
|
|
|
expect(substr_count($token, '.'))->toBe(2);
|
|
|
|
// ...and verifiable with the generated public key (proves the pair matches).
|
|
$claims = JWT::decode($token, new Key(File::get($this->publicKeyPath), 'ES256'));
|
|
|
|
expect($claims->sub)->toBe('100.64.0.10');
|
|
});
|
|
|
|
it('refuses to overwrite an existing private key without --force', function () {
|
|
File::ensureDirectoryExists($this->keyDir);
|
|
File::put($this->privateKeyPath, 'SENTINEL-PRIVATE-KEY');
|
|
|
|
$this->artisan('v5:flux-generate-keys')
|
|
->expectsOutputToContain('already exists')
|
|
->assertSuccessful();
|
|
|
|
expect(File::get($this->privateKeyPath))->toBe('SENTINEL-PRIVATE-KEY');
|
|
});
|
|
|
|
it('overwrites an existing private key with --force and produces a valid keypair', function () {
|
|
File::ensureDirectoryExists($this->keyDir);
|
|
File::put($this->privateKeyPath, 'SENTINEL-PRIVATE-KEY');
|
|
|
|
$this->artisan('v5:flux-generate-keys --force')->assertSuccessful();
|
|
|
|
expect(File::get($this->privateKeyPath))->not->toBe('SENTINEL-PRIVATE-KEY')
|
|
->and(fluxKeyMode($this->privateKeyPath))->toBe('0600');
|
|
|
|
$token = app(AgentTokenIssuer::class)->issue('100.64.0.10');
|
|
|
|
expect(substr_count($token, '.'))->toBe(2);
|
|
});
|
|
|
|
it('prints the public key when --show-public is passed', function () {
|
|
$this->artisan('v5:flux-generate-keys --show-public')
|
|
->expectsOutputToContain('BEGIN PUBLIC KEY')
|
|
->assertSuccessful();
|
|
});
|