diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 06b9f2d24..601c364de 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -2884,6 +2884,10 @@ class ApplicationsController extends Controller ], 422); } + $requestHasHttpBasicAuth = $request->has('is_http_basic_auth_enabled') + || $request->has('http_basic_auth_username') + || $request->has('http_basic_auth_password'); + if ($request->has('is_http_basic_auth_enabled') && $request->is_http_basic_auth_enabled === true) { if (blank($application->http_basic_auth_username) || blank($application->http_basic_auth_password)) { $validationErrors = []; @@ -2901,10 +2905,6 @@ class ApplicationsController extends Controller } } } - if ($request->has('is_http_basic_auth_enabled') && $application->is_container_label_readonly_enabled === false) { - $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); - $application->save(); - } // For dockercompose applications, domains (fqdn) field should not be used // Only docker_compose_domains should be used to set domains for individual services @@ -3119,7 +3119,7 @@ class ApplicationsController extends Controller // Must run after fqdn is filled: flags are kept only for domains the app still has. $application->setNoindexDomains($request->input('noindex_domains') ?? []); } - if ($application->settings->is_container_label_readonly_enabled && ($requestHasDomains || $requestHasNoindexDomains) && $server->isProxyShouldRun()) { + if ($application->settings->is_container_label_readonly_enabled && ($requestHasDomains || $requestHasNoindexDomains || $requestHasHttpBasicAuth) && $server->isProxyShouldRun()) { $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); } $application->save(); diff --git a/tests/Feature/Api/ApplicationSettingsApiTest.php b/tests/Feature/Api/ApplicationSettingsApiTest.php index 6f54e57e8..c1d08bf38 100644 --- a/tests/Feature/Api/ApplicationSettingsApiTest.php +++ b/tests/Feature/Api/ApplicationSettingsApiTest.php @@ -14,6 +14,8 @@ use Illuminate\Support\Facades\Queue; uses(RefreshDatabase::class); beforeEach(function () { + config(['app.maintenance.driver' => 'file']); + InstanceSettings::unguarded(fn () => InstanceSettings::firstOrCreate(['id' => 0])); $this->team = Team::factory()->create(); @@ -136,6 +138,53 @@ test('proxy settings regenerate managed labels', function () { expect(base64_decode($this->application->fresh()->custom_labels))->not->toContain('sentinel-label=true'); }); +test('http basic auth updates regenerate managed labels', function () { + $this->application->settings->update(['is_container_label_readonly_enabled' => true]); + $this->application->update([ + 'fqdn' => 'https://app.example.com', + 'is_http_basic_auth_enabled' => false, + 'http_basic_auth_username' => null, + 'http_basic_auth_password' => null, + 'custom_labels' => base64_encode('sentinel-label=true'), + ]); + + $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken)) + ->patchJson("/api/v1/applications/{$this->application->uuid}", [ + 'is_http_basic_auth_enabled' => true, + 'http_basic_auth_username' => 'api-user', + 'http_basic_auth_password' => 'api-password', + ]) + ->assertOk(); + + $application = $this->application->fresh(); + $labels = $application->parseContainerLabels(); + + expect((bool) $application->is_http_basic_auth_enabled)->toBeTrue() + ->and($application->http_basic_auth_username)->toBe('api-user') + ->and($application->http_basic_auth_password)->toBe('api-password') + ->and($labels)->toContain('basicauth') + ->and($labels)->toContain('api-user') + ->and($labels)->not->toContain('sentinel-label=true'); +}); + +test('http basic auth updates preserve user-managed labels', function () { + $this->application->settings->update(['is_container_label_readonly_enabled' => false]); + $this->application->update([ + 'fqdn' => 'https://app.example.com', + 'custom_labels' => base64_encode('sentinel-label=true'), + ]); + + $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken)) + ->patchJson("/api/v1/applications/{$this->application->uuid}", [ + 'is_http_basic_auth_enabled' => true, + 'http_basic_auth_username' => 'api-user', + 'http_basic_auth_password' => 'api-password', + ]) + ->assertOk(); + + expect(base64_decode($this->application->fresh()->custom_labels))->toBe('sentinel-label=true'); +}); + test('rejects invalid boolean application settings', function () { $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken)) ->patchJson("/api/v1/applications/{$this->application->uuid}", [