fix: update resource forms

This commit is contained in:
Andras Bacsai 2026-08-28 15:08:25 +02:00
parent 0dfbc42c0f
commit 8d675f2e21
3 changed files with 125 additions and 17 deletions

View file

@ -76,7 +76,9 @@ private function syncData(bool $toModel = false): void
// Sync FROM model (on load/refresh) // Sync FROM model (on load/refresh)
$this->name = $this->private_key->name; $this->name = $this->private_key->name;
$this->description = $this->private_key->description; $this->description = $this->private_key->description;
$this->privateKeyValue = $this->private_key->private_key; $this->privateKeyValue = auth()->user()->can('update', $this->private_key)
? $this->private_key->private_key
: '';
$this->isGitRelated = $this->private_key->is_git_related; $this->isGitRelated = $this->private_key->is_git_related;
} }
} }

View file

@ -122,13 +122,6 @@ public function updatedHtmlUrl(): void
} }
} }
public function boot()
{
if ($this->github_app) {
$this->github_app->makeVisible(['client_secret', 'webhook_secret']);
}
}
/** /**
* Sync data between component properties and model * Sync data between component properties and model
* *
@ -170,8 +163,9 @@ private function syncData(bool $toModel = false): void
$this->appId = $this->github_app->app_id; $this->appId = $this->github_app->app_id;
$this->installationId = $this->github_app->installation_id; $this->installationId = $this->github_app->installation_id;
$this->clientId = $this->github_app->client_id; $this->clientId = $this->github_app->client_id;
$this->clientSecret = $this->github_app->client_secret; $canUpdate = auth()->user()->can('update', $this->github_app);
$this->webhookSecret = $this->github_app->webhook_secret; $this->clientSecret = $canUpdate ? $this->github_app->client_secret : null;
$this->webhookSecret = $canUpdate ? $this->github_app->webhook_secret : null;
$this->isSystemWide = $this->github_app->is_system_wide; $this->isSystemWide = $this->github_app->is_system_wide;
$this->privateKeyId = $this->github_app->private_key_id; $this->privateKeyId = $this->github_app->private_key_id;
$this->contents = $this->github_app->contents; $this->contents = $this->github_app->contents;
@ -231,7 +225,7 @@ public function checkPermissions()
syncGithubAppName($this->github_app); syncGithubAppName($this->github_app);
GithubAppPermissionJob::dispatchSync($this->github_app); GithubAppPermissionJob::dispatchSync($this->github_app);
$this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->github_app->refresh();
$this->syncData(false); $this->syncData(false);
$this->isConnected = $this->github_app->isConnected(); $this->isConnected = $this->github_app->isConnected();
$this->name = str($this->github_app->name)->kebab(); $this->name = str($this->github_app->name)->kebab();
@ -305,7 +299,7 @@ public function mount()
try { try {
$github_app_uuid = request()->github_app_uuid; $github_app_uuid = request()->github_app_uuid;
$this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail(); $this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail();
$this->github_app->makeVisible(['client_secret', 'webhook_secret']); $this->authorize('view', $this->github_app);
$this->privateKeys = PrivateKey::ownedByCurrentTeamCached(); $this->privateKeys = PrivateKey::ownedByCurrentTeamCached();
$this->applications = $this->github_app->applications; $this->applications = $this->github_app->applications;
@ -420,7 +414,6 @@ public function submit()
try { try {
$this->authorize('update', $this->github_app); $this->authorize('update', $this->github_app);
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
$this->organization = normalizeGithubOrganization($this->organization); $this->organization = normalizeGithubOrganization($this->organization);
$this->apiUrl = filled($this->apiUrl) $this->apiUrl = filled($this->apiUrl)
? $this->apiUrl ? $this->apiUrl
@ -442,7 +435,6 @@ public function createGithubAppManually()
{ {
$this->authorize('update', $this->github_app); $this->authorize('update', $this->github_app);
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
$this->github_app->app_id = 1234567890; $this->github_app->app_id = 1234567890;
$this->github_app->installation_id = 1234567890; $this->github_app->installation_id = 1234567890;
$this->github_app->save(); $this->github_app->save();
@ -457,8 +449,6 @@ public function instantSave()
try { try {
$this->authorize('update', $this->github_app); $this->authorize('update', $this->github_app);
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
$this->syncData(true); $this->syncData(true);
$this->github_app->save(); $this->github_app->save();
$this->isConnected = $this->github_app->isConnected(); $this->isConnected = $this->github_app->isConnected();
@ -475,7 +465,6 @@ public function delete()
if ($this->github_app->applications->isNotEmpty()) { if ($this->github_app->applications->isNotEmpty()) {
$this->dispatch('error', 'This source is being used by an application. Please delete all applications first.'); $this->dispatch('error', 'This source is being used by an application. Please delete all applications first.');
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
return; return;
} }

View file

@ -0,0 +1,117 @@
<?php
use App\Livewire\Security\PrivateKey\Show as PrivateKeyShow;
use App\Livewire\Source\Github\Change as GithubAppChange;
use App\Models\GithubApp;
use App\Models\InstanceSettings;
use App\Models\PrivateKey;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Livewire\Livewire;
uses(RefreshDatabase::class);
beforeEach(function () {
InstanceSettings::forceCreate(['id' => 0]);
Storage::fake('ssh-keys');
$this->team = Team::factory()->create();
$this->owner = User::factory()->create();
$this->member = User::factory()->create();
$this->team->members()->attach($this->owner, ['role' => 'owner']);
$this->team->members()->attach($this->member, ['role' => 'member']);
});
it('does not serialize a private key for a member who cannot update it', function () {
$privateKeyValue = PrivateKey::generateNewKeyPair('ed25519')['private_key'];
$privateKey = PrivateKey::factory()->create([
'team_id' => $this->team->id,
'private_key' => $privateKeyValue,
]);
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
Livewire::test(PrivateKeyShow::class, ['private_key_uuid' => $privateKey->uuid])
->assertSuccessful()
->assertSet('privateKeyValue', '')
->assertDontSee($privateKeyValue);
});
it('keeps a private key available to an owner who can update it', function () {
$privateKeyValue = PrivateKey::generateNewKeyPair('ed25519')['private_key'];
$privateKey = PrivateKey::factory()->create([
'team_id' => $this->team->id,
'private_key' => $privateKeyValue,
]);
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
Livewire::test(PrivateKeyShow::class, ['private_key_uuid' => $privateKey->uuid])
->assertSuccessful()
->assertSet('privateKeyValue', $privateKeyValue);
});
it('does not serialize GitHub App secrets for a member who cannot update it', function () {
$githubApp = createGithubAppWithSecrets($this->team);
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
Livewire::withQueryParams(['github_app_uuid' => $githubApp->uuid])
->test(GithubAppChange::class)
->assertSuccessful()
->assertSet('clientSecret', null)
->assertSet('webhookSecret', null)
->assertDontSee('stored-client-secret')
->assertDontSee('stored-webhook-secret');
});
it('does not serialize secrets from a system-wide GitHub App for another team', function () {
$githubApp = createGithubAppWithSecrets($this->team, isSystemWide: true);
$otherTeam = Team::factory()->create();
$otherUser = User::factory()->create();
$otherTeam->members()->attach($otherUser, ['role' => 'owner']);
$this->actingAs($otherUser);
session(['currentTeam' => $otherTeam]);
Livewire::withQueryParams(['github_app_uuid' => $githubApp->uuid])
->test(GithubAppChange::class)
->assertSuccessful()
->assertSet('clientSecret', null)
->assertSet('webhookSecret', null)
->assertDontSee('stored-client-secret')
->assertDontSee('stored-webhook-secret');
});
it('keeps GitHub App secrets available to an owner who can update it', function () {
$githubApp = createGithubAppWithSecrets($this->team);
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
Livewire::withQueryParams(['github_app_uuid' => $githubApp->uuid])
->test(GithubAppChange::class)
->assertSuccessful()
->assertSet('clientSecret', 'stored-client-secret')
->assertSet('webhookSecret', 'stored-webhook-secret');
});
function createGithubAppWithSecrets(Team $team, bool $isSystemWide = false): GithubApp
{
return GithubApp::query()->create([
'team_id' => $team->id,
'name' => 'security-test-app',
'api_url' => 'https://api.github.com',
'html_url' => 'https://github.com',
'custom_user' => 'git',
'custom_port' => 22,
'client_secret' => 'stored-client-secret',
'webhook_secret' => 'stored-webhook-secret',
'is_system_wide' => $isSystemWide,
]);
}