From 55b1a1468f4e28dcd87c8b352315fe9093829131 Mon Sep 17 00:00:00 2001 From: nexxo Date: Sun, 26 Jul 2026 05:34:57 +0200 Subject: [PATCH] fix(admin): the admin password is shown once, and means it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The handoff stayed in the cache for its full ten minutes, so any replayed component request could fetch the plaintext again — "shown once" was a figure of speech. It is consumed on the first read and the token dropped; the payload reaches the view and nothing else. Co-Authored-By: Claude Opus 4.8 --- app/Livewire/Admin/InstanceAdminAccess.php | 14 +++++++++++- .../Feature/Admin/InstanceAdminAccessTest.php | 22 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Admin/InstanceAdminAccess.php b/app/Livewire/Admin/InstanceAdminAccess.php index 97053c1..ba52e75 100644 --- a/app/Livewire/Admin/InstanceAdminAccess.php +++ b/app/Livewire/Admin/InstanceAdminAccess.php @@ -82,9 +82,21 @@ class InstanceAdminAccess extends ModalComponent public function render() { $payload = null; + if ($this->token !== null) { $raw = ConfigHandoff::get($this->token); - $payload = $raw === null ? null : json_decode($raw, true); + + if ($raw !== null) { + // Consumed on the first read: leaving it in the cache would make + // "shown once" a figure of speech — any replayed component + // request could fetch the password again for ten minutes. The + // payload lives in a local variable, so it reaches the view and + // nothing else. + ConfigHandoff::forget($this->token); + $this->token = null; + $this->waiting = false; + $payload = json_decode($raw, true); + } } return view('livewire.admin.instance-admin-access', [ diff --git a/tests/Feature/Admin/InstanceAdminAccessTest.php b/tests/Feature/Admin/InstanceAdminAccessTest.php index 62a8f2c..1490711 100644 --- a/tests/Feature/Admin/InstanceAdminAccessTest.php +++ b/tests/Feature/Admin/InstanceAdminAccessTest.php @@ -114,3 +114,25 @@ it('tells the console when Proxmox is unreachable', function () { // Otherwise the modal polls forever with a spinner. expect(json_decode(ConfigHandoff::get('handoff-throw'), true))->toBe(['error' => 'failed']); }); + +it('shows the password once and not a second time', function () { + $pve = new FakeProxmoxClient; + app()->instance(ProxmoxClient::class, $pve); + $instance = adminAccessInstance(); + $owner = operator('Owner'); + $owner->forceFill(['password' => Hash::make('geheim-1234')])->save(); + + $modal = Livewire\Livewire::actingAs($owner) + ->test(InstanceAdminAccess::class, ['uuid' => $instance->uuid]) + ->set('password', 'geheim-1234') + ->call('request'); // sync queue: the job runs immediately + + $token = $modal->get('token'); + $modal->assertSee(__('instances.admin_ready')); + + // "Shown once" has to mean it: a replayed request must not fetch it again. + expect(ConfigHandoff::get($token ?? 'none'))->toBeNull() + ->and($modal->get('token'))->toBeNull(); + + $modal->call('$refresh')->assertDontSee(__('instances.admin_ready')); +});