From 847166a3f89b7c80972fa0d2e5c754976f95b6ad Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:56:37 +0100 Subject: [PATCH] fix(terminal): apply authorization middleware to terminal bootstrap routes Apply the existing `can.access.terminal` middleware to `POST /terminal/auth` and `POST /terminal/auth/ips` routes, consistent with the `GET /terminal` route. Adds regression tests covering unauthenticated, member, admin, and owner roles. Co-Authored-By: Claude Opus 4.6 --- routes/web.php | 4 +- .../TerminalAuthRoutesAuthorizationTest.php | 118 ++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/TerminalAuthRoutesAuthorizationTest.php diff --git a/routes/web.php b/routes/web.php index 27763f121..4154fefab 100644 --- a/routes/web.php +++ b/routes/web.php @@ -164,7 +164,7 @@ } return response()->json(['authenticated' => false], 401); - })->name('terminal.auth'); + })->name('terminal.auth')->middleware('can.access.terminal'); Route::post('/terminal/auth/ips', function () { if (auth()->check()) { @@ -189,7 +189,7 @@ } return response()->json(['ipAddresses' => []], 401); - })->name('terminal.auth.ips'); + })->name('terminal.auth.ips')->middleware('can.access.terminal'); Route::prefix('invitations')->group(function () { Route::get('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept'); diff --git a/tests/Feature/TerminalAuthRoutesAuthorizationTest.php b/tests/Feature/TerminalAuthRoutesAuthorizationTest.php new file mode 100644 index 000000000..858cc7101 --- /dev/null +++ b/tests/Feature/TerminalAuthRoutesAuthorizationTest.php @@ -0,0 +1,118 @@ +set('app.env', 'local'); + + $this->team = Team::factory()->create(); + + $this->privateKey = PrivateKey::create([ + 'name' => 'Test Key', + 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk +hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA +AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV +uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== +-----END OPENSSH PRIVATE KEY-----', + 'team_id' => $this->team->id, + ]); + + Server::factory()->create([ + 'name' => 'Test Server', + 'ip' => 'coolify-testing-host', + 'team_id' => $this->team->id, + 'private_key_id' => $this->privateKey->id, + ]); +}); + +// --- POST /terminal/auth --- + +it('denies unauthenticated users on POST /terminal/auth', function () { + $this->postJson('/terminal/auth') + ->assertStatus(401); +}); + +it('denies non-admin team members on POST /terminal/auth', function () { + $member = User::factory()->create(); + $member->teams()->attach($this->team, ['role' => 'member']); + + $this->actingAs($member); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth') + ->assertStatus(403); +}); + +it('allows team owners on POST /terminal/auth', function () { + $owner = User::factory()->create(); + $owner->teams()->attach($this->team, ['role' => 'owner']); + + $this->actingAs($owner); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth') + ->assertStatus(200) + ->assertJson(['authenticated' => true]); +}); + +it('allows team admins on POST /terminal/auth', function () { + $admin = User::factory()->create(); + $admin->teams()->attach($this->team, ['role' => 'admin']); + + $this->actingAs($admin); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth') + ->assertStatus(200) + ->assertJson(['authenticated' => true]); +}); + +// --- POST /terminal/auth/ips --- + +it('denies unauthenticated users on POST /terminal/auth/ips', function () { + $this->postJson('/terminal/auth/ips') + ->assertStatus(401); +}); + +it('denies non-admin team members on POST /terminal/auth/ips', function () { + $member = User::factory()->create(); + $member->teams()->attach($this->team, ['role' => 'member']); + + $this->actingAs($member); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth/ips') + ->assertStatus(403); +}); + +it('allows team owners on POST /terminal/auth/ips', function () { + $owner = User::factory()->create(); + $owner->teams()->attach($this->team, ['role' => 'owner']); + + $this->actingAs($owner); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth/ips') + ->assertStatus(200) + ->assertJsonStructure(['ipAddresses']); +}); + +it('allows team admins on POST /terminal/auth/ips', function () { + $admin = User::factory()->create(); + $admin->teams()->attach($this->team, ['role' => 'admin']); + + $this->actingAs($admin); + session(['currentTeam' => $this->team]); + + $this->postJson('/terminal/auth/ips') + ->assertStatus(200) + ->assertJsonStructure(['ipAddresses']); +});