From d443758b030cf00e23f7e9746d0a120875814ea4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 26 May 2026 17:36:02 +0200 Subject: [PATCH] fix(github): allow system-wide private apps across teams Use the shared GitHub app scope when listing and loading private apps so system-wide apps owned by another team remain available. Update coverage for mounting and loading repositories through those apps. --- .../Project/New/GithubPrivateRepository.php | 4 +- tests/Feature/GithubPrivateRepositoryTest.php | 42 ++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index c292e254c..1c9c8e896 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -71,7 +71,7 @@ class GithubPrivateRepository extends Component $this->parameters = get_route_parameters(); $this->query = request()->query(); $this->repositories = $this->branches = collect(); - $this->github_apps = GithubApp::where('team_id', currentTeam()->id) + $this->github_apps = GithubApp::ownedByCurrentTeam() ->where('is_public', false) ->whereNotNull('app_id') ->get(); @@ -106,7 +106,7 @@ class GithubPrivateRepository extends Component $this->total_branches_count = 0; $this->page = 1; $this->selected_github_app_id = $github_app_id; - $this->github_app = GithubApp::where('team_id', currentTeam()->id) + $this->github_app = GithubApp::ownedByCurrentTeam() ->where('is_public', false) ->whereNotNull('app_id') ->findOrFail($github_app_id); diff --git a/tests/Feature/GithubPrivateRepositoryTest.php b/tests/Feature/GithubPrivateRepositoryTest.php index 5da17065d..781d29b3d 100644 --- a/tests/Feature/GithubPrivateRepositoryTest.php +++ b/tests/Feature/GithubPrivateRepositoryTest.php @@ -127,7 +127,7 @@ describe('GitHub Private Repository Component', function () { Http::assertNothingSent(); }); - test('loadRepositories does not mint tokens for another teams system wide github app', function () { + test('mount lists another teams system wide github app', function () { $victimTeam = Team::factory()->create(); $victimPrivateKey = githubPrivateRepositoryTestPrivateKeyForTeam($victimTeam); $systemWideGithubApp = GithubApp::create([ @@ -147,13 +147,43 @@ describe('GitHub Private Repository Component', function () { 'is_system_wide' => true, ]); - Http::fake(); + $component = Livewire::test(GithubPrivateRepository::class, ['type' => 'private-gh-app']); - expect(fn () => Livewire::test(GithubPrivateRepository::class, ['type' => 'private-gh-app']) + expect($component->get('github_apps')->pluck('id')->all()) + ->toContain($this->githubApp->id) + ->toContain($systemWideGithubApp->id); + }); + + test('loadRepositories can use another teams system wide github app', function () { + $victimTeam = Team::factory()->create(); + $victimPrivateKey = githubPrivateRepositoryTestPrivateKeyForTeam($victimTeam); + $systemWideGithubApp = GithubApp::create([ + 'name' => 'System Wide GitHub App', + 'api_url' => 'https://api.github.com', + 'html_url' => 'https://github.com', + 'custom_user' => 'git', + 'custom_port' => 22, + 'app_id' => 54321, + 'installation_id' => 67890, + 'client_id' => 'system-client-id', + 'client_secret' => 'system-client-secret', + 'webhook_secret' => 'system-webhook-secret', + 'private_key_id' => $victimPrivateKey->id, + 'team_id' => $victimTeam->id, + 'is_public' => false, + 'is_system_wide' => true, + ]); + $repos = [ + ['id' => 1, 'name' => 'system-repo', 'owner' => ['login' => 'testuser']], + ]; + + fakeGithubHttp($repos); + + Livewire::test(GithubPrivateRepository::class, ['type' => 'private-gh-app']) ->call('loadRepositories', $systemWideGithubApp->id) - )->toThrow(ModelNotFoundException::class); - - Http::assertNothingSent(); + ->assertSet('current_step', 'repository') + ->assertSet('total_repositories_count', 1) + ->assertSet('selected_repository_id', 1); }); test('github installation token is not stored as public component state', function () {