From df543a4414d1641422a45262837435429ddae9d6 Mon Sep 17 00:00:00 2001 From: nala Date: Fri, 13 Nov 2026 13:41:48 +0200 Subject: [PATCH] Recognise designated superusers in authenticated demography installation --- app/Services/DemographicImport.php | 3 ++- routes/demography.php | 5 +++-- tests/Feature/DemographicInstallationTest.php | 11 +++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/Services/DemographicImport.php b/app/Services/DemographicImport.php index f92bff2f..35ad4cd5 100644 --- a/app/Services/DemographicImport.php +++ b/app/Services/DemographicImport.php @@ -15,7 +15,8 @@ final class DemographicImport { public function authorize(User $actor): void { - abort_unless($actor->can('manage-portal') && ! app(PortalProfileAccess::class)->has($actor, 'auditor'), 403); + $access = app(PortalProfileAccess::class); + abort_unless(($actor->can('manage-portal') || $access->superuser($actor)) && ! $access->has($actor, 'auditor'), 403); abort_unless($actor->two_factor_confirmed_at && $actor->two_factor_secret, 403, 'Confirme MFA para gerir dados demográficos.'); if (! IsolatedQaDatabase::allowsSchemaChanges()) { abort_unless(app(DemographicInstallation::class)->activeTarget() && app(DemographicEvidence::class)->installed(), 403, 'Destino demográfico não instalado ou não autorizado.'); diff --git a/routes/demography.php b/routes/demography.php index a88c693e..d44111ca 100644 --- a/routes/demography.php +++ b/routes/demography.php @@ -1,13 +1,14 @@ middleware('throttle:30,1'); Route::get('/api/v1/investment/demography', [DemographyController::class, 'index'])->middleware('throttle:30,1'); -Route::prefix('management/demography')->middleware(['auth', 'verified', EnsureScopedPortalAdministration::class, PrivateInvestmentResponse::class, 'throttle:20,1'])->group(function () { +// Each action enforces its existing installer/importer authorisation. The legacy +// catalogue gate does not recognise a designated profile superuser. +Route::prefix('management/demography')->middleware(['auth', 'verified', PrivateInvestmentResponse::class, 'throttle:20,1'])->group(function () { Route::get('/install', [DemographyController::class, 'installation']); Route::post('/install', [DemographyController::class, 'install']); Route::get('/', [DemographyController::class, 'management']); diff --git a/tests/Feature/DemographicInstallationTest.php b/tests/Feature/DemographicInstallationTest.php index 896eb723..538d49ba 100644 --- a/tests/Feature/DemographicInstallationTest.php +++ b/tests/Feature/DemographicInstallationTest.php @@ -44,6 +44,17 @@ final class DemographicInstallationTest extends TestCase $this->actingAs(User::factory()->create())->getJson('/management/demography/install')->assertForbidden(); } + public function test_designated_superuser_does_not_require_the_legacy_manager_gate(): void + { + config(['portal.managers' => []]); + $this->assertFalse($this->manager->can('manage-portal')); + $this->actingAs($this->manager)->withSession(['portal.mfa_actor' => $this->manager->id, 'portal.mfa_at' => time()]); + $this->get('/management/demography/install')->assertOk()->assertInertia(fn (\Inertia\Testing\AssertableInertia $page) => $page + ->component('management/demography-install')->where('plan.mfa_confirmed', true)); + app(\App\Services\DemographicImport::class)->authorize($this->manager); + $this->assertTrue(true); + } + public function test_an_unavailable_interpreter_blocks_the_plan_before_installation(): void { config(['demography.python' => base_path('storage/nonexistent-demography-python.exe')]);