diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index 310d4219a1..38484ef6f3 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -133,12 +133,10 @@ class Index extends Component } if ($this->selectedExistingPrivateKey) { - $this->createdPrivateKey = PrivateKey::where('team_id', currentTeam()->id) - ->where('id', $this->selectedExistingPrivateKey) - ->first(); + $this->createdPrivateKey = PrivateKey::ownedByCurrentTeam(['team_id']) + ->find($this->selectedExistingPrivateKey); if ($this->createdPrivateKey) { - $this->privateKey = $this->createdPrivateKey->private_key; - $this->publicKey = $this->createdPrivateKey->getPublicKey(); + $this->authorize('update', $this->createdPrivateKey); } } @@ -249,9 +247,9 @@ class Index extends Component return; } - $this->createdPrivateKey = PrivateKey::ownedByCurrentTeam()->findOrFail($this->selectedExistingPrivateKey); - $this->authorize('view', $this->createdPrivateKey); - $this->privateKey = $this->createdPrivateKey->private_key; + $this->createdPrivateKey = PrivateKey::ownedByCurrentTeam(['team_id'])->findOrFail($this->selectedExistingPrivateKey); + $this->authorize('update', $this->createdPrivateKey); + $this->privateKey = null; $this->currentState = 'create-server'; } @@ -305,14 +303,13 @@ class Index extends Component $this->validate(); - $this->privateKey = formatPrivateKey($this->privateKey); $foundServer = Server::whereIp($this->remoteServerHost)->first(); if ($foundServer) { return $this->dispatch('error', 'A server with this IP/Domain already exists.'); } $privateKeyId = $this->createdPrivateKey?->id ?? $this->selectedExistingPrivateKey; - $this->createdPrivateKey = PrivateKey::ownedByCurrentTeam()->findOrFail($privateKeyId); - $this->authorize('view', $this->createdPrivateKey); + $this->createdPrivateKey = PrivateKey::ownedByCurrentTeam(['team_id'])->findOrFail($privateKeyId); + $this->authorize('update', $this->createdPrivateKey); try { $this->createdServer = Team::createServerWithinLimit(currentTeam()->id, [ diff --git a/tests/Feature/BoardingAuthorizationTest.php b/tests/Feature/BoardingAuthorizationTest.php index bc7c738068..7945a6524d 100644 --- a/tests/Feature/BoardingAuthorizationTest.php +++ b/tests/Feature/BoardingAuthorizationTest.php @@ -151,6 +151,75 @@ test('a client supplied private key identifier from another team is rejected by ->call('selectExistingPrivateKey'); })->throws(ModelNotFoundException::class); +test('a member cannot load a team private key through onboarding query parameters', function () { + $member = User::factory()->create(); + $member->teams()->attach($this->otherTeam, ['role' => 'member']); + $this->otherTeam->update(['show_boarding' => false]); + actAsBoardingUser($member, $this->otherTeam); + + Livewire::test(Index::class, [ + 'selectedServerType' => 'remote', + 'selectedExistingPrivateKey' => $this->otherKey->id, + ])->assertForbidden(); +}); + +test('a member cannot select a team private key through an onboarding action', function () { + $member = User::factory()->create(); + $member->teams()->attach($this->otherTeam, ['role' => 'member']); + $this->otherTeam->update(['show_boarding' => false]); + actAsBoardingUser($member, $this->otherTeam); + + Livewire::test(Index::class) + ->set('selectedExistingPrivateKey', $this->otherKey->id) + ->call('selectExistingPrivateKey') + ->assertForbidden(); +}); + +test('an owner can select an existing key without exposing its private value', function () { + actAsBoardingUser($this->otherOwner, $this->otherTeam); + + $component = Livewire::test(Index::class) + ->set('selectedExistingPrivateKey', $this->otherKey->id) + ->call('selectExistingPrivateKey') + ->assertOk() + ->assertSet('createdPrivateKey.id', $this->otherKey->id) + ->assertSet('privateKey', null); + + expect($component->get('createdPrivateKey')->getAttributes())->not->toHaveKey('private_key'); +}); + +test('an owner can resume onboarding with an existing key without exposing its private value', function () { + actAsBoardingUser($this->otherOwner, $this->otherTeam); + + $component = Livewire::test(Index::class, [ + 'selectedServerType' => 'remote', + 'selectedExistingPrivateKey' => $this->otherKey->id, + ]) + ->assertOk() + ->assertSet('createdPrivateKey.id', $this->otherKey->id) + ->assertSet('privateKey', null); + + expect($component->get('createdPrivateKey')->getAttributes())->not->toHaveKey('private_key'); +}); + +test('an owner can create a server with an existing key without loading its private value', function () { + actAsBoardingUser($this->otherOwner, $this->otherTeam); + + Livewire::test(Index::class) + ->set('selectedExistingPrivateKey', $this->otherKey->id) + ->call('selectExistingPrivateKey') + ->set('remoteServerName', 'Existing key server') + ->set('remoteServerHost', '192.0.2.51') + ->set('remoteServerPort', 22) + ->set('remoteServerUser', 'root') + ->call('saveServer') + ->assertOk() + ->assertSet('currentState', 'validate-server') + ->assertSet('privateKey', null); + + expect(Server::query()->where('ip', '192.0.2.51')->firstOrFail()->private_key_id)->toBe($this->otherKey->id); +}); + test('a hydrated server model from another team is forbidden before use', function () { actAsBoardingUser($this->rootOwner, $this->rootTeam);