clusev/tests/Feature/ConfirmServerScopeTest.php

150 lines
5.6 KiB
PHP

<?php
namespace Tests\Feature;
use App\Livewire\Files\Index as Files;
use App\Livewire\Servers\Show;
use App\Livewire\Services\Index as Services;
use App\Models\Server;
use App\Models\User;
use App\Services\FirewallService;
use App\Services\FleetService;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;
/**
* Server-scoped confirm tokens must not be retargetable to another host/path.
* A token sealed for server A, replayed against a handler acting on server B,
* is a no-op — the destructive SSH/SFTP call never runs.
*/
class ConfirmServerScopeTest extends TestCase
{
use RefreshDatabase;
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
private function server(string $name, string $ip): Server
{
return Server::create(['name' => $name, 'ip' => $ip, 'ssh_port' => 22, 'status' => 'online']);
}
private function confirmed(string $event, array $params, ?int $serverId): string
{
$token = ConfirmToken::issue($event, $params, '', null, $serverId);
ConfirmToken::confirm($token); // the modal confirmation step
return $token;
}
public function test_key_removal_token_for_another_server_is_rejected(): void
{
$this->actingAs(User::factory()->create());
$serverA = $this->server('a', '10.0.0.1');
$serverB = $this->server('b', '10.0.0.2');
$fleet = Mockery::mock(FleetService::class);
$fleet->shouldReceive('removeAuthorizedKey')->never();
app()->instance(FleetService::class, $fleet);
$token = $this->confirmed('keyRemoved', ['fingerprint' => 'ab:cd'], $serverA->id);
// Handler is bound to server B; the token was sealed for A.
Livewire::test(Show::class, ['server' => $serverB])->call('removeKey', $token);
$this->assertTrue(true); // Mockery verifies removeAuthorizedKey was never called
}
public function test_key_removal_token_for_this_server_proceeds(): void
{
$this->actingAs(User::factory()->create());
$server = $this->server('a', '10.0.0.1');
$fleet = Mockery::mock(FleetService::class);
$fleet->shouldReceive('removeAuthorizedKey')->once()
->with(Mockery::on(fn ($s) => $s->id === $server->id), 'ab:cd');
$fleet->shouldReceive('sshKeys')->andReturn([]); // reloadKeys() after success
app()->instance(FleetService::class, $fleet);
$token = $this->confirmed('keyRemoved', ['fingerprint' => 'ab:cd'], $server->id);
Livewire::test(Show::class, ['server' => $server])
->call('removeKey', $token)
->assertSet('sshKeys', []); // reloadKeys() ran → success path reached
}
public function test_file_delete_token_for_another_server_is_rejected(): void
{
$this->actingAs(User::factory()->create());
$serverA = $this->server('a', '10.0.0.1');
$serverB = $this->server('b', '10.0.0.2');
session(['active_server_id' => $serverB->id]);
$fleet = Mockery::mock(FleetService::class);
$fleet->shouldReceive('files')->andReturn([]); // any dir listing during mount/load
$fleet->shouldReceive('deleteFile')->never();
app()->instance(FleetService::class, $fleet);
$token = $this->confirmed('fileConfirmed', ['path' => '/etc/passwd'], $serverA->id);
Livewire::test(Files::class)->call('deleteEntry', $token);
$this->assertTrue(true); // Mockery verifies deleteFile was never called
}
public function test_credential_deletion_token_for_another_server_is_rejected(): void
{
$this->actingAs(User::factory()->create());
$serverA = $this->server('a', '10.0.0.1');
$serverB = $this->server('b', '10.0.0.2');
$serverB->credential()->create(['username' => 'root', 'auth_type' => 'password', 'secret' => 'x']);
$token = $this->confirmed('credentialDeleted', [], $serverA->id);
Livewire::test(Show::class, ['server' => $serverB])->call('deleteCredential', $token);
$this->assertTrue($serverB->credential()->exists(), "B's credential must survive a server-A token");
}
public function test_firewall_rule_deletion_token_for_another_server_is_rejected(): void
{
$this->actingAs(User::factory()->create());
$serverA = $this->server('a', '10.0.0.1');
$serverB = $this->server('b', '10.0.0.2');
$firewall = Mockery::mock(FirewallService::class);
$firewall->shouldReceive('deleteRule')->never();
app()->instance(FirewallService::class, $firewall);
$token = $this->confirmed('firewallRuleDelete', ['spec' => '22/tcp', 'label' => 'SSH'], $serverA->id);
Livewire::test(Show::class, ['server' => $serverB])->call('deleteFirewallRule', $token);
$this->assertTrue(true); // Mockery verifies deleteRule was never called
}
public function test_service_action_token_for_another_server_is_rejected(): void
{
$this->actingAs(User::factory()->create());
$serverA = $this->server('a', '10.0.0.1');
$serverB = $this->server('b', '10.0.0.2');
session(['active_server_id' => $serverB->id]);
$fleet = Mockery::mock(FleetService::class);
$fleet->shouldReceive('serviceAction')->never();
app()->instance(FleetService::class, $fleet);
$token = $this->confirmed('serviceConfirmed', ['op' => 'stop', 'name' => 'nginx.service'], $serverA->id);
Livewire::test(Services::class)->call('applyService', $token);
$this->assertTrue(true); // Mockery verifies serviceAction was never called
}
}