Files
coolify/tests/Feature/Api/PrivateKeyUpdateApiTest.php
033f0c84da v4.7 (#12478)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
Co-authored-by: Aditya Tripathi <aditya@climactic.co>
2026-11-06 14:25:02 +01:00

76 lines
3.3 KiB
PHP

<?php
use App\Models\InstanceSettings;
use App\Models\PrivateKey;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Testing\TestResponse;
uses(RefreshDatabase::class);
beforeEach(function () {
config()->set('app.maintenance.store', 'array');
config()->set('cache.default', 'array');
InstanceSettings::forceCreate(['id' => 0, 'is_api_enabled' => true]);
$this->team = Team::factory()->create();
$this->user = User::factory()->create();
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
session(['currentTeam' => $this->team]);
$this->keyMaterial = generateSSHKey('ed25519')['private'];
$this->key = PrivateKey::create(['name' => 'Old name', 'private_key' => $this->keyMaterial, 'team_id' => $this->team->id]);
$token = $this->user->createToken('write-token', ['write']);
$token->accessToken->forceFill(['team_id' => $this->team->id])->save();
$this->bearerToken = $token->plainTextToken;
});
function patchPrivateKeyUpdateApi(object $test, string $uuid, string $bearerToken, array $payload): TestResponse
{
return $test->withHeaders(['Authorization' => 'Bearer '.$bearerToken])
->patchJson('/api/v1/security/keys/'.$uuid, $payload);
}
test('a private key can be renamed without sending the key again', function () {
patchPrivateKeyUpdateApi($this, $this->key->uuid, $this->bearerToken, ['name' => 'New name', 'description' => 'Renamed'])
->assertCreated();
$fresh = $this->key->fresh();
expect($fresh->name)->toBe('New name')
->and($fresh->description)->toBe('Renamed')
->and($fresh->private_key)->toBe($this->key->private_key)
->and($fresh->fingerprint)->toBe($this->key->fingerprint);
});
test('a private key update still replaces and validates the key when it is sent', function () {
$newKey = generateSSHKey('ed25519')['private'];
patchPrivateKeyUpdateApi($this, $this->key->uuid, $this->bearerToken, ['private_key' => $newKey])->assertCreated();
expect($this->key->fresh()->fingerprint)->toBe(PrivateKey::generateFingerprint($newKey));
patchPrivateKeyUpdateApi($this, $this->key->uuid, $this->bearerToken, ['private_key' => 'not a key'])->assertUnprocessable();
expect($this->key->fresh()->fingerprint)->toBe(PrivateKey::generateFingerprint($newKey));
});
test('a rejected key returns JSON 422 also when the client does not ask for JSON', function () {
$this->call('PATCH', '/api/v1/security/keys/'.$this->key->uuid, server: [
'CONTENT_TYPE' => 'application/json',
'HTTP_AUTHORIZATION' => 'Bearer '.$this->bearerToken,
], content: json_encode(['private_key' => 'not a key']))
->assertUnprocessable()
->assertJson(['message' => 'The private key is invalid. Paste an unencrypted private key, not a public key or a certificate.']);
expect($this->key->fresh()->fingerprint)->toBe(PrivateKey::generateFingerprint($this->keyMaterial));
});
test('a private key of another team cannot be renamed', function () {
$otherTeam = Team::factory()->create();
$foreign = PrivateKey::create(['name' => 'Theirs', 'private_key' => generateSSHKey('ed25519')['private'], 'team_id' => $otherTeam->id]);
patchPrivateKeyUpdateApi($this, $foreign->uuid, $this->bearerToken, ['name' => 'Hijacked'])->assertNotFound();
expect($foreign->fresh()->name)->toBe('Theirs');
});