From 386539ee3043390610880bb51b2167323bead179 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:08:14 +0200 Subject: [PATCH] fix(gitea): only instance admins can make a Gitea source system wide Co-Authored-By: Claude Opus 5.5 --- app/Livewire/Source/Gitea/Change.php | 9 ++ app/Livewire/Source/Gitea/Create.php | 4 + app/Policies/GiteaAppPolicy.php | 8 ++ .../GiteaSystemWideAuthorizationTest.php | 103 ++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 tests/Feature/GiteaSystemWideAuthorizationTest.php diff --git a/app/Livewire/Source/Gitea/Change.php b/app/Livewire/Source/Gitea/Change.php index e66cc355cb..b09487e716 100644 --- a/app/Livewire/Source/Gitea/Change.php +++ b/app/Livewire/Source/Gitea/Change.php @@ -264,10 +264,18 @@ class Change extends Component } } + private function authorizeSystemWideChange(): void + { + if ($this->isSystemWide && ! $this->gitea_app->is_system_wide) { + $this->authorize('makeSystemWide', GiteaApp::class); + } + } + public function submit() { try { $this->authorize('update', $this->gitea_app); + $this->authorizeSystemWideChange(); $this->validate(); @@ -283,6 +291,7 @@ class Change extends Component { try { $this->authorize('update', $this->gitea_app); + $this->authorizeSystemWideChange(); $this->validateOnly('isSystemWide'); diff --git a/app/Livewire/Source/Gitea/Create.php b/app/Livewire/Source/Gitea/Create.php index 5b7e2a7719..a3c1247db9 100644 --- a/app/Livewire/Source/Gitea/Create.php +++ b/app/Livewire/Source/Gitea/Create.php @@ -67,6 +67,10 @@ class Create extends Component 'organization' => 'nullable|string', ]); + if ($this->is_system_wide) { + $this->authorize('makeSystemWide', GiteaApp::class); + } + $gitea_app = GiteaApp::create([ 'name' => $this->name, 'api_url' => $this->api_url, diff --git a/app/Policies/GiteaAppPolicy.php b/app/Policies/GiteaAppPolicy.php index a269ead557..6def71365e 100644 --- a/app/Policies/GiteaAppPolicy.php +++ b/app/Policies/GiteaAppPolicy.php @@ -35,6 +35,14 @@ class GiteaAppPolicy return $user->isAdmin(); } + /** + * Determine whether the user can make a Gitea App available to every team on the instance. + */ + public function makeSystemWide(User $user): bool + { + return $user->canAccessSystemResources(); + } + /** * Determine whether the user can update the model. */ diff --git a/tests/Feature/GiteaSystemWideAuthorizationTest.php b/tests/Feature/GiteaSystemWideAuthorizationTest.php new file mode 100644 index 0000000000..c85cee627d --- /dev/null +++ b/tests/Feature/GiteaSystemWideAuthorizationTest.php @@ -0,0 +1,103 @@ + 0]); + + $this->team = Team::factory()->create(); + $this->user = User::factory()->create(); + $this->team->members()->attach($this->user->id, ['role' => 'owner']); + + $this->actingAs($this->user); + session(['currentTeam' => $this->team]); + + $this->giteaApp = GiteaApp::create([ + 'name' => 'Team Gitea', + 'api_url' => 'https://gitea.com/api/v1', + 'html_url' => 'https://gitea.com', + 'custom_user' => 'git', + 'custom_port' => 22, + 'team_id' => $this->team->id, + 'is_system_wide' => false, + 'is_public' => false, + ]); +}); + +function joinRootTeam(User $user): void +{ + $rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]); + $rootTeam->members()->attach($user->id, ['role' => 'admin']); + $user->load('teams'); +} + +describe('team admin outside the root team', function () { + test('cannot make a source system wide with instantSave', function () { + Livewire::withQueryParams(['gitea_app_uuid' => $this->giteaApp->uuid]) + ->test(Change::class) + ->set('isSystemWide', true) + ->call('instantSave'); + + expect($this->giteaApp->refresh()->is_system_wide)->toBeFalse(); + }); + + test('cannot make a source system wide with submit', function () { + Livewire::withQueryParams(['gitea_app_uuid' => $this->giteaApp->uuid]) + ->test(Change::class) + ->set('isSystemWide', true) + ->call('submit'); + + expect($this->giteaApp->refresh()->is_system_wide)->toBeFalse(); + }); + + test('cannot create a system wide source', function () { + Livewire::test(Create::class) + ->set('name', 'sneaky-gitea') + ->set('is_system_wide', true) + ->call('createGiteaApp'); + + expect(GiteaApp::where('name', 'sneaky-gitea')->exists())->toBeFalse(); + }); + + test('can still save a team source that stays team scoped', function () { + Livewire::withQueryParams(['gitea_app_uuid' => $this->giteaApp->uuid]) + ->test(Change::class) + ->set('name', 'Renamed Gitea') + ->call('submit') + ->assertDispatched('success'); + + expect($this->giteaApp->refresh()->name)->toBe('Renamed Gitea'); + }); +}); + +describe('root team member', function () { + beforeEach(fn () => joinRootTeam($this->user)); + + test('can make a source system wide with instantSave', function () { + Livewire::withQueryParams(['gitea_app_uuid' => $this->giteaApp->uuid]) + ->test(Change::class) + ->set('isSystemWide', true) + ->call('instantSave') + ->assertDispatched('success'); + + expect($this->giteaApp->refresh()->is_system_wide)->toBeTrue(); + }); + + test('can create a system wide source', function () { + Livewire::test(Create::class) + ->set('name', 'shared-gitea') + ->set('is_system_wide', true) + ->call('createGiteaApp'); + + expect(GiteaApp::where('name', 'shared-gitea')->value('is_system_wide'))->toBeTrue(); + }); +});