2026-03-19 18:25:19 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MintyPHP\Tests\Architecture;
|
|
|
|
|
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
|
|
|
|
|
class AuthzAdminSettingsContractTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
use ProjectFileAssertionSupport;
|
|
|
|
|
|
|
|
|
|
public function testAdminSettingsActionsUseCentralPolicies(): void
|
|
|
|
|
{
|
refactor(settings): split admin/settings into hub + per-section subpages
Splits the 755-line monolithic settings page into a tile-based landing hub
and six focused subpages (general, security, email, api, sso, branding),
each with its own form, CSRF scope and POST handler. Each subpage offers
Save / Save & close buttons plus a Cancel/back link to the hub.
Backend (AdminSettingsService, gateways, policies, DB schema) unchanged.
A new settingsSectionMergePost() helper overlays section POSTs onto the
current DB values so partial saves don't wipe unrelated fields (the
service defaults missing keys to 0/empty).
Sub-action files (logo/favicon/tokens/lifecycle) redirect to the matching
subpage, and architecture contracts now check the subpage files instead
of the removed monolithic index.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:43:12 +02:00
|
|
|
// Landing hub: VIEW only (no POST handler).
|
2026-03-19 18:25:19 +01:00
|
|
|
$indexContent = $this->readProjectFile('pages/admin/settings/index().php');
|
|
|
|
|
$this->assertStringContainsString('AuthorizationService::class', $indexContent);
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_VIEW', $indexContent);
|
refactor(settings): split admin/settings into hub + per-section subpages
Splits the 755-line monolithic settings page into a tile-based landing hub
and six focused subpages (general, security, email, api, sso, branding),
each with its own form, CSRF scope and POST handler. Each subpage offers
Save / Save & close buttons plus a Cancel/back link to the hub.
Backend (AdminSettingsService, gateways, policies, DB schema) unchanged.
A new settingsSectionMergePost() helper overlays section POSTs onto the
current DB values so partial saves don't wipe unrelated fields (the
service defaults missing keys to 0/empty).
Sub-action files (logo/favicon/tokens/lifecycle) redirect to the matching
subpage, and architecture contracts now check the subpage files instead
of the removed monolithic index.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:43:12 +02:00
|
|
|
|
|
|
|
|
// Subpages that expose a settings form: VIEW + UPDATE.
|
2026-04-25 08:50:05 +02:00
|
|
|
foreach (['general', 'account-access', 'user-lifecycle', 'audit', 'telemetry', 'email', 'api', 'sso'] as $section) {
|
refactor(settings): split admin/settings into hub + per-section subpages
Splits the 755-line monolithic settings page into a tile-based landing hub
and six focused subpages (general, security, email, api, sso, branding),
each with its own form, CSRF scope and POST handler. Each subpage offers
Save / Save & close buttons plus a Cancel/back link to the hub.
Backend (AdminSettingsService, gateways, policies, DB schema) unchanged.
A new settingsSectionMergePost() helper overlays section POSTs onto the
current DB values so partial saves don't wipe unrelated fields (the
service defaults missing keys to 0/empty).
Sub-action files (logo/favicon/tokens/lifecycle) redirect to the matching
subpage, and architecture contracts now check the subpage files instead
of the removed monolithic index.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:43:12 +02:00
|
|
|
$content = $this->readProjectFile("pages/admin/settings/{$section}().php");
|
|
|
|
|
$this->assertStringContainsString('AuthorizationService::class', $content, $section);
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_VIEW', $content, $section);
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_UPDATE', $content, $section);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Branding landing: VIEW only (uploads post to separate action files).
|
|
|
|
|
$brandingIndex = $this->readProjectFile('pages/admin/settings/branding().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_VIEW', $brandingIndex);
|
2026-03-19 18:25:19 +01:00
|
|
|
|
|
|
|
|
$logoUpload = $this->readProjectFile('pages/admin/settings/logo().php');
|
|
|
|
|
$this->assertStringContainsString('AuthorizationService::class', $logoUpload);
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_BRANDING_UPDATE', $logoUpload);
|
|
|
|
|
|
|
|
|
|
$logoDelete = $this->readProjectFile('pages/admin/settings/logo-delete().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_BRANDING_UPDATE', $logoDelete);
|
|
|
|
|
|
|
|
|
|
$faviconUpload = $this->readProjectFile('pages/admin/settings/favicon().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_BRANDING_UPDATE', $faviconUpload);
|
|
|
|
|
|
|
|
|
|
$faviconDelete = $this->readProjectFile('pages/admin/settings/favicon-delete().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_BRANDING_UPDATE', $faviconDelete);
|
|
|
|
|
|
|
|
|
|
$revokeApiTokens = $this->readProjectFile('pages/admin/settings/revoke-api-tokens().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_TOKENS_REVOKE', $revokeApiTokens);
|
|
|
|
|
|
|
|
|
|
$expireRememberTokens = $this->readProjectFile('pages/admin/settings/expire-remember-tokens().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_TOKENS_REVOKE', $expireRememberTokens);
|
|
|
|
|
|
|
|
|
|
$runUserLifecycle = $this->readProjectFile('pages/admin/settings/run-user-lifecycle().php');
|
|
|
|
|
$this->assertStringContainsString('SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_USER_LIFECYCLE_RUN', $runUserLifecycle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testScheduledJobEditUsesAbilityChecksAndNoPermissionHelper(): void
|
|
|
|
|
{
|
|
|
|
|
$content = $this->readProjectFile('pages/admin/scheduled-jobs/edit($id).php');
|
|
|
|
|
$this->assertStringContainsString('OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW', $content);
|
|
|
|
|
$this->assertStringContainsString('OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_MANAGE', $content);
|
|
|
|
|
$this->assertStringContainsString('OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_RUN_NOW', $content);
|
|
|
|
|
$this->assertStringNotContainsString("can('jobs.manage')", $content);
|
|
|
|
|
$this->assertStringNotContainsString("can('jobs.run_now')", $content);
|
|
|
|
|
}
|
|
|
|
|
}
|