106 lines
4.2 KiB
PHP
106 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Server;
|
|
use App\Services\FleetService;
|
|
use App\Services\HardeningService;
|
|
use App\Services\SshKeyProvisioner;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
class SshKeyProvisionerTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
Mockery::close();
|
|
parent::tearDown();
|
|
}
|
|
|
|
private function passwordServer(): Server
|
|
{
|
|
$server = Server::create(['name' => 'box', 'ip' => '10.0.0.5', 'ssh_port' => 22, 'status' => 'online']);
|
|
$server->credential()->create(['username' => 'admin', 'auth_type' => 'password', 'secret' => 'pw-secret']);
|
|
|
|
return $server->fresh('credential');
|
|
}
|
|
|
|
public function test_happy_path_installs_key_switches_credential_and_disables_password(): void
|
|
{
|
|
$server = $this->passwordServer();
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldReceive('addAuthorizedKey')->once();
|
|
$fleet->shouldReceive('testConnection')->once()->andReturn(['ok' => true, 'error' => null]);
|
|
|
|
$hardening = Mockery::mock(HardeningService::class);
|
|
$hardening->shouldReceive('apply')->once()->with(Mockery::type(Server::class), 'ssh_password', false)
|
|
->andReturn(['ok' => true, 'output' => '']);
|
|
|
|
$result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server);
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertNotEmpty($result['privateKey']);
|
|
$this->assertStringContainsString('ssh-ed25519', $result['publicKey']);
|
|
|
|
$cred = $server->fresh('credential')->credential;
|
|
$this->assertSame('key', $cred->auth_type);
|
|
$this->assertStringContainsString('PRIVATE KEY', $cred->secret); // now holds the private PEM
|
|
}
|
|
|
|
public function test_verify_failure_rolls_back_the_credential_and_does_not_disable_password(): void
|
|
{
|
|
$server = $this->passwordServer();
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldReceive('addAuthorizedKey')->once();
|
|
$fleet->shouldReceive('testConnection')->once()->andReturn(['ok' => false, 'error' => 'auth failed']);
|
|
|
|
$hardening = Mockery::mock(HardeningService::class);
|
|
$hardening->shouldNotReceive('apply'); // never disable password if the key didn't verify
|
|
|
|
$result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server);
|
|
|
|
$this->assertFalse($result['ok']);
|
|
$this->assertNotEmpty($result['error']);
|
|
|
|
$cred = $server->fresh('credential')->credential;
|
|
$this->assertSame('password', $cred->auth_type, 'credential restored to password');
|
|
$this->assertSame('pw-secret', $cred->secret, 'original secret restored');
|
|
}
|
|
|
|
public function test_already_key_credential_skips_keygen_and_just_disables_password(): void
|
|
{
|
|
$server = Server::create(['name' => 'box2', 'ip' => '10.0.0.6', 'ssh_port' => 22, 'status' => 'online']);
|
|
$server->credential()->create(['username' => 'admin', 'auth_type' => 'key', 'secret' => 'EXISTING-PRIVATE-KEY']);
|
|
$server = $server->fresh('credential');
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldNotReceive('addAuthorizedKey'); // already key — no new key generated
|
|
$fleet->shouldNotReceive('testConnection');
|
|
|
|
$hardening = Mockery::mock(HardeningService::class);
|
|
$hardening->shouldReceive('apply')->once()->with(Mockery::type(Server::class), 'ssh_password', false)
|
|
->andReturn(['ok' => true, 'output' => '']);
|
|
|
|
$result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server);
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertArrayNotHasKey('privateKey', $result); // nothing new to reveal
|
|
$this->assertSame('EXISTING-PRIVATE-KEY', $server->fresh('credential')->credential->secret);
|
|
}
|
|
|
|
public function test_no_credential_returns_error(): void
|
|
{
|
|
$server = Server::create(['name' => 'box3', 'ip' => '10.0.0.7', 'ssh_port' => 22, 'status' => 'online']);
|
|
|
|
$result = (new SshKeyProvisioner(Mockery::mock(FleetService::class), Mockery::mock(HardeningService::class)))
|
|
->enableKeyOnlyAccess($server);
|
|
|
|
$this->assertFalse($result['ok']);
|
|
}
|
|
}
|