feat(settings): add admin session policy management
This commit is contained in:
@@ -1017,6 +1017,15 @@
|
||||
"Import": "Import",
|
||||
"Counters": "Zähler",
|
||||
"User lifecycle policy": "Benutzer-Lifecycle-Regel",
|
||||
"Session policy": "Sitzungsrichtlinie",
|
||||
"Session limits define how long logged-in users stay signed in.": "Sitzungslimits bestimmen, wie lange angemeldete Benutzer eingeloggt bleiben.",
|
||||
"Idle timeout signs users out after no activity.": "Das Inaktivitäts-Timeout meldet Benutzer bei fehlender Aktivität ab.",
|
||||
"Absolute timeout signs users out after total session duration, even with activity.": "Das absolute Timeout meldet Benutzer nach der gesamten Sitzungsdauer ab, auch bei Aktivität.",
|
||||
"New limits apply on each user's next request.": "Neue Limits gelten mit der nächsten Anfrage jedes Benutzers.",
|
||||
"Session idle timeout (minutes)": "Sitzungs-Inaktivitäts-Timeout (Minuten)",
|
||||
"Session absolute timeout (hours)": "Absolutes Sitzungs-Timeout (Stunden)",
|
||||
"Allowed range: 5-1440 minutes (default: 30)": "Erlaubter Bereich: 5-1440 Minuten (Standard: 30)",
|
||||
"Allowed range: 1-72 hours (default: 8)": "Erlaubter Bereich: 1-72 Stunden (Standard: 8)",
|
||||
"Deactivate users after inactivity (days)": "Benutzer nach Inaktivität deaktivieren (Tage)",
|
||||
"Delete inactive users after (days)": "Inaktive Benutzer löschen nach (Tagen)",
|
||||
"0 disables this rule": "0 deaktiviert diese Regel",
|
||||
@@ -1026,11 +1035,15 @@
|
||||
"Run user lifecycle now": "Benutzer-Lifecycle jetzt ausführen",
|
||||
"setting.user_inactivity_deactivate_days": "Setzt Benutzer nach dieser Anzahl Tagen ohne Login automatisch auf inaktiv",
|
||||
"setting.user_inactivity_delete_days": "Löscht inaktive Benutzer nach dieser zusätzlichen Anzahl Tagen automatisch",
|
||||
"setting.session_idle_timeout_minutes": "Maximale Inaktivitätsdauer in Minuten vor automatischer Abmeldung (Standard: 30)",
|
||||
"setting.session_absolute_timeout_hours": "Maximale Gesamtdauer einer Sitzung in Stunden vor automatischer Abmeldung (Standard: 8)",
|
||||
"User lifecycle already running": "Benutzer-Lifecycle läuft bereits",
|
||||
"User lifecycle failed": "Benutzer-Lifecycle fehlgeschlagen",
|
||||
"User lifecycle completed: %d deactivated, %d deleted": "Benutzer-Lifecycle abgeschlossen: %d deaktiviert, %d gelöscht",
|
||||
"User inactivity deactivation period is invalid": "Zeitraum für automatische Deaktivierung ist ungültig",
|
||||
"User inactivity deletion period is invalid": "Zeitraum für automatische Löschung ist ungültig",
|
||||
"Session idle timeout is invalid": "Sitzungs-Inaktivitäts-Timeout ist ungültig",
|
||||
"Session absolute timeout is invalid": "Absolutes Sitzungs-Timeout ist ungültig",
|
||||
"Auto-delete is ignored while auto-deactivate is disabled": "Auto-Löschung wird ignoriert, solange Auto-Deaktivierung deaktiviert ist",
|
||||
"Scheduled jobs": "Geplante Aufgaben",
|
||||
"Can view scheduled jobs": "Kann geplante Aufgaben anzeigen",
|
||||
|
||||
@@ -1017,6 +1017,15 @@
|
||||
"Import": "Import",
|
||||
"Counters": "Counters",
|
||||
"User lifecycle policy": "User lifecycle policy",
|
||||
"Session policy": "Session policy",
|
||||
"Session limits define how long logged-in users stay signed in.": "Session limits define how long logged-in users stay signed in.",
|
||||
"Idle timeout signs users out after no activity.": "Idle timeout signs users out after no activity.",
|
||||
"Absolute timeout signs users out after total session duration, even with activity.": "Absolute timeout signs users out after total session duration, even with activity.",
|
||||
"New limits apply on each user's next request.": "New limits apply on each user's next request.",
|
||||
"Session idle timeout (minutes)": "Session idle timeout (minutes)",
|
||||
"Session absolute timeout (hours)": "Session absolute timeout (hours)",
|
||||
"Allowed range: 5-1440 minutes (default: 30)": "Allowed range: 5-1440 minutes (default: 30)",
|
||||
"Allowed range: 1-72 hours (default: 8)": "Allowed range: 1-72 hours (default: 8)",
|
||||
"Deactivate users after inactivity (days)": "Deactivate users after inactivity (days)",
|
||||
"Delete inactive users after (days)": "Delete inactive users after (days)",
|
||||
"0 disables this rule": "0 disables this rule",
|
||||
@@ -1026,11 +1035,15 @@
|
||||
"Run user lifecycle now": "Run user lifecycle now",
|
||||
"setting.user_inactivity_deactivate_days": "Automatically set users inactive after this many days without login",
|
||||
"setting.user_inactivity_delete_days": "Automatically delete inactive users after this many additional days",
|
||||
"setting.session_idle_timeout_minutes": "Maximum idle time in minutes before users are signed out (default: 30)",
|
||||
"setting.session_absolute_timeout_hours": "Maximum total session duration in hours before users are signed out (default: 8)",
|
||||
"User lifecycle already running": "User lifecycle already running",
|
||||
"User lifecycle failed": "User lifecycle failed",
|
||||
"User lifecycle completed: %d deactivated, %d deleted": "User lifecycle completed: %d deactivated, %d deleted",
|
||||
"User inactivity deactivation period is invalid": "User inactivity deactivation period is invalid",
|
||||
"User inactivity deletion period is invalid": "User inactivity deletion period is invalid",
|
||||
"Session idle timeout is invalid": "Session idle timeout is invalid",
|
||||
"Session absolute timeout is invalid": "Session absolute timeout is invalid",
|
||||
"Auto-delete is ignored while auto-deactivate is disabled": "Auto-delete is ignored while auto-deactivate is disabled",
|
||||
"Scheduled jobs": "Scheduled jobs",
|
||||
"Can view scheduled jobs": "Can view scheduled jobs",
|
||||
|
||||
@@ -48,6 +48,7 @@ final class SettingsRegistrar implements ContainerRegistrar
|
||||
$c->get(SettingsAppGateway::class),
|
||||
$c->get(SettingsApiPolicyGateway::class),
|
||||
$c->get(SettingsUserLifecycleGateway::class),
|
||||
$c->get(SettingsSessionGateway::class),
|
||||
$c->get(SettingsSystemAuditGateway::class),
|
||||
$c->get(SettingsFrontendTelemetryGateway::class),
|
||||
$c->get(SettingsMicrosoftGateway::class),
|
||||
|
||||
@@ -16,6 +16,7 @@ class AdminSettingsService
|
||||
private readonly SettingsAppGateway $settingsAppGateway,
|
||||
private readonly SettingsApiPolicyGateway $settingsApiPolicyGateway,
|
||||
private readonly SettingsUserLifecycleGateway $settingsUserLifecycleGateway,
|
||||
private readonly SettingsSessionGateway $settingsSessionGateway,
|
||||
private readonly SettingsSystemAuditGateway $settingsSystemAuditGateway,
|
||||
private readonly SettingsFrontendTelemetryGateway $settingsFrontendTelemetryGateway,
|
||||
private readonly SettingsMicrosoftGateway $settingsMicrosoftGateway,
|
||||
@@ -61,6 +62,8 @@ class AdminSettingsService
|
||||
'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
|
||||
'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
|
||||
'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
|
||||
'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
|
||||
'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
|
||||
'api_cors_allowed_origins' => $this->settingsApiPolicyGateway->getApiCorsAllowedOriginsText(),
|
||||
'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
|
||||
'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
|
||||
@@ -90,6 +93,8 @@ class AdminSettingsService
|
||||
'api_token_max_ttl_days' => $this->settingsMetadataGateway->get(SettingKeys::API_TOKEN_MAX_TTL_DAYS_KEY),
|
||||
'user_inactivity_deactivate_days' => $this->settingsMetadataGateway->get(SettingKeys::USER_INACTIVITY_DEACTIVATE_DAYS_KEY),
|
||||
'user_inactivity_delete_days' => $this->settingsMetadataGateway->get(SettingKeys::USER_INACTIVITY_DELETE_DAYS_KEY),
|
||||
'session_idle_timeout_minutes' => $this->settingsMetadataGateway->get(SettingKeys::SESSION_IDLE_TIMEOUT_MINUTES_KEY),
|
||||
'session_absolute_timeout_hours' => $this->settingsMetadataGateway->get(SettingKeys::SESSION_ABSOLUTE_TIMEOUT_HOURS_KEY),
|
||||
'api_cors_allowed_origins' => $this->settingsMetadataGateway->get(SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY),
|
||||
'system_audit_enabled' => $this->settingsMetadataGateway->get(SettingKeys::SYSTEM_AUDIT_ENABLED_KEY),
|
||||
'system_audit_retention_days' => $this->settingsMetadataGateway->get(SettingKeys::SYSTEM_AUDIT_RETENTION_DAYS_KEY),
|
||||
@@ -126,6 +131,8 @@ class AdminSettingsService
|
||||
$apiTokenMaxTtlDays = (int) ($post['api_token_max_ttl_days'] ?? 0);
|
||||
$userInactivityDeactivateDays = (int) ($post['user_inactivity_deactivate_days'] ?? 0);
|
||||
$userInactivityDeleteDays = (int) ($post['user_inactivity_delete_days'] ?? 0);
|
||||
$sessionIdleTimeoutMinutes = (int) ($post['session_idle_timeout_minutes'] ?? 0);
|
||||
$sessionAbsoluteTimeoutHours = (int) ($post['session_absolute_timeout_hours'] ?? 0);
|
||||
$apiCorsAllowedOrigins = $this->normalizeScalarText($post['api_cors_allowed_origins'] ?? '');
|
||||
$systemAuditEnabled = isset($post['system_audit_enabled']);
|
||||
$systemAuditRetentionDays = (int) ($post['system_audit_retention_days'] ?? 0);
|
||||
@@ -158,6 +165,8 @@ class AdminSettingsService
|
||||
'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
|
||||
'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
|
||||
'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
|
||||
'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
|
||||
'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
|
||||
'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
|
||||
'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
|
||||
'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
|
||||
@@ -209,6 +218,20 @@ class AdminSettingsService
|
||||
$this->settingsUserLifecycleGateway->setUserInactivityDeleteDays(0);
|
||||
}
|
||||
|
||||
$sessionIdleOk = $this->settingsSessionGateway->setSessionIdleTimeoutMinutes(
|
||||
$sessionIdleTimeoutMinutes > 0 ? $sessionIdleTimeoutMinutes : null
|
||||
);
|
||||
if (!$sessionIdleOk) {
|
||||
$errors[] = ['message' => 'Session idle timeout is invalid', 'key' => 'session_idle_timeout_invalid'];
|
||||
}
|
||||
|
||||
$sessionAbsoluteOk = $this->settingsSessionGateway->setSessionAbsoluteTimeoutHours(
|
||||
$sessionAbsoluteTimeoutHours > 0 ? $sessionAbsoluteTimeoutHours : null
|
||||
);
|
||||
if (!$sessionAbsoluteOk) {
|
||||
$errors[] = ['message' => 'Session absolute timeout is invalid', 'key' => 'session_absolute_timeout_invalid'];
|
||||
}
|
||||
|
||||
$apiCorsOriginsOk = $this->settingsApiPolicyGateway->setApiCorsAllowedOrigins($apiCorsAllowedOrigins);
|
||||
if (!$apiCorsOriginsOk) {
|
||||
$errors[] = ['message' => 'CORS allowed origins are invalid', 'key' => 'api_cors_allowed_origins_invalid'];
|
||||
@@ -311,6 +334,8 @@ class AdminSettingsService
|
||||
'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
|
||||
'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
|
||||
'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
|
||||
'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
|
||||
'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
|
||||
'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
|
||||
'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
|
||||
'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
|
||||
|
||||
@@ -26,6 +26,8 @@ $apiTokenDefaultTtlDays = (int) ($values['api_token_default_ttl_days'] ?? 90);
|
||||
$apiTokenMaxTtlDays = (int) ($values['api_token_max_ttl_days'] ?? 365);
|
||||
$userInactivityDeactivateDays = (int) ($values['user_inactivity_deactivate_days'] ?? 180);
|
||||
$userInactivityDeleteDays = (int) ($values['user_inactivity_delete_days'] ?? 365);
|
||||
$sessionIdleTimeoutMinutes = (int) ($values['session_idle_timeout_minutes'] ?? 30);
|
||||
$sessionAbsoluteTimeoutHours = (int) ($values['session_absolute_timeout_hours'] ?? 8);
|
||||
$apiCorsAllowedOrigins = (string) ($values['api_cors_allowed_origins'] ?? '');
|
||||
$systemAuditEnabled = !empty($values['system_audit_enabled']);
|
||||
$systemAuditRetentionDays = (int) ($values['system_audit_retention_days'] ?? 365);
|
||||
@@ -70,6 +72,8 @@ $apiTokenDefaultTtlDaysDesc = $settings['api_token_default_ttl_days']['descripti
|
||||
$apiTokenMaxTtlDaysDesc = $settings['api_token_max_ttl_days']['description'] ?? 'setting.api_token_max_ttl_days';
|
||||
$userInactivityDeactivateDaysDesc = $settings['user_inactivity_deactivate_days']['description'] ?? 'setting.user_inactivity_deactivate_days';
|
||||
$userInactivityDeleteDaysDesc = $settings['user_inactivity_delete_days']['description'] ?? 'setting.user_inactivity_delete_days';
|
||||
$sessionIdleTimeoutMinutesDesc = $settings['session_idle_timeout_minutes']['description'] ?? 'setting.session_idle_timeout_minutes';
|
||||
$sessionAbsoluteTimeoutHoursDesc = $settings['session_absolute_timeout_hours']['description'] ?? 'setting.session_absolute_timeout_hours';
|
||||
$apiCorsAllowedOriginsDesc = $settings['api_cors_allowed_origins']['description'] ?? 'setting.api_cors_allowed_origins';
|
||||
$systemAuditEnabledDesc = $settings['system_audit_enabled']['description'] ?? 'setting.system_audit_enabled';
|
||||
$systemAuditRetentionDaysDesc = $settings['system_audit_retention_days']['description'] ?? 'setting.system_audit_retention_days';
|
||||
@@ -324,6 +328,34 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
|
||||
<?php endif; ?>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<fieldset>
|
||||
<legend><small><?php e(t('Session policy')); ?></small></legend>
|
||||
<blockquote data-variant="info">
|
||||
<?php e(t('Session limits define how long logged-in users stay signed in.')); ?>
|
||||
</blockquote>
|
||||
<small class="muted"><?php e(t('Idle timeout signs users out after no activity.')); ?></small>
|
||||
<small class="muted"><?php e(t('Absolute timeout signs users out after total session duration, even with activity.')); ?></small>
|
||||
<small class="muted"><?php e(t('New limits apply on each user\'s next request.')); ?></small>
|
||||
<div class="grid">
|
||||
<label class="app-field">
|
||||
<span><?php e(t('Session idle timeout (minutes)')); ?></span>
|
||||
<input type="number" name="session_idle_timeout_minutes"
|
||||
value="<?php e((string) $sessionIdleTimeoutMinutes); ?>" min="5" max="1440" step="1"
|
||||
<?php e($readonlyAttr); ?>>
|
||||
<small><?php e(t($sessionIdleTimeoutMinutesDesc)); ?></small>
|
||||
<small class="muted"><?php e(t('Allowed range: 5-1440 minutes (default: 30)')); ?></small>
|
||||
</label>
|
||||
<label class="app-field">
|
||||
<span><?php e(t('Session absolute timeout (hours)')); ?></span>
|
||||
<input type="number" name="session_absolute_timeout_hours"
|
||||
value="<?php e((string) $sessionAbsoluteTimeoutHours); ?>" min="1" max="72" step="1"
|
||||
<?php e($readonlyAttr); ?>>
|
||||
<small><?php e(t($sessionAbsoluteTimeoutHoursDesc)); ?></small>
|
||||
<small class="muted"><?php e(t('Allowed range: 1-72 hours (default: 8)')); ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<fieldset>
|
||||
<legend><small><?php e(t('System audit')); ?></small></legend>
|
||||
<label class="app-field">
|
||||
|
||||
294
tests/Service/Settings/AdminSettingsServiceSessionPolicyTest.php
Normal file
294
tests/Service/Settings/AdminSettingsServiceSessionPolicyTest.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Settings;
|
||||
|
||||
use MintyPHP\Repository\Auth\ApiTokenRepository;
|
||||
use MintyPHP\Repository\Auth\RememberTokenRepository;
|
||||
use MintyPHP\Service\Access\RoleService;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Org\DepartmentService;
|
||||
use MintyPHP\Service\Settings\AdminSettingsService;
|
||||
use MintyPHP\Service\Settings\SettingCacheService;
|
||||
use MintyPHP\Service\Settings\SettingsApiPolicyGateway;
|
||||
use MintyPHP\Service\Settings\SettingsAppGateway;
|
||||
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
|
||||
use MintyPHP\Service\Settings\SettingsFrontendTelemetryGateway;
|
||||
use MintyPHP\Service\Settings\SettingsMetadataGateway;
|
||||
use MintyPHP\Service\Settings\SettingsMicrosoftGateway;
|
||||
use MintyPHP\Service\Settings\SettingsSessionGateway;
|
||||
use MintyPHP\Service\Settings\SettingsSmtpGateway;
|
||||
use MintyPHP\Service\Settings\SettingsSystemAuditGateway;
|
||||
use MintyPHP\Service\Settings\SettingsUserLifecycleGateway;
|
||||
use MintyPHP\Service\Tenant\TenantService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AdminSettingsServiceSessionPolicyTest extends TestCase
|
||||
{
|
||||
public function testBuildPageDataIncludesSessionPolicyValuesAndMetadata(): void
|
||||
{
|
||||
$settingsSessionGateway = $this->createConfiguredMock(SettingsSessionGateway::class, [
|
||||
'getSessionIdleTimeoutMinutes' => 45,
|
||||
'getSessionAbsoluteTimeoutHours' => 12,
|
||||
'setSessionIdleTimeoutMinutes' => true,
|
||||
'setSessionAbsoluteTimeoutHours' => true,
|
||||
]);
|
||||
|
||||
$settingsMetadataGateway = $this->createMock(SettingsMetadataGateway::class);
|
||||
$settingsMetadataGateway->method('getValue')->willReturn(null);
|
||||
$settingsMetadataGateway->method('get')->willReturnCallback(
|
||||
static fn (string $key): array => ['key' => $key, 'description' => 'desc-' . $key]
|
||||
);
|
||||
|
||||
$service = $this->newService($settingsSessionGateway, $settingsMetadataGateway);
|
||||
$data = $service->buildPageData();
|
||||
|
||||
$values = is_array($data['values'] ?? null) ? $data['values'] : [];
|
||||
$settings = is_array($data['settings'] ?? null) ? $data['settings'] : [];
|
||||
|
||||
$this->assertSame(45, (int) ($values['session_idle_timeout_minutes'] ?? 0));
|
||||
$this->assertSame(12, (int) ($values['session_absolute_timeout_hours'] ?? 0));
|
||||
$this->assertSame(
|
||||
'desc-session_idle_timeout_minutes',
|
||||
(string) (($settings['session_idle_timeout_minutes'] ?? [])['description'] ?? '')
|
||||
);
|
||||
$this->assertSame(
|
||||
'desc-session_absolute_timeout_hours',
|
||||
(string) (($settings['session_absolute_timeout_hours'] ?? [])['description'] ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
public function testUpdateFromAdminPersistsValidSessionPolicyValues(): void
|
||||
{
|
||||
$settingsSessionGateway = $this->createMock(SettingsSessionGateway::class);
|
||||
$settingsSessionGateway->method('getSessionIdleTimeoutMinutes')->willReturn(30);
|
||||
$settingsSessionGateway->method('getSessionAbsoluteTimeoutHours')->willReturn(8);
|
||||
$settingsSessionGateway->expects($this->once())
|
||||
->method('setSessionIdleTimeoutMinutes')
|
||||
->with(15)
|
||||
->willReturn(true);
|
||||
$settingsSessionGateway->expects($this->once())
|
||||
->method('setSessionAbsoluteTimeoutHours')
|
||||
->with(12)
|
||||
->willReturn(true);
|
||||
|
||||
$service = $this->newService($settingsSessionGateway);
|
||||
$result = $service->updateFromAdmin($this->validPostData([
|
||||
'session_idle_timeout_minutes' => '15',
|
||||
'session_absolute_timeout_hours' => '12',
|
||||
]));
|
||||
|
||||
$this->assertSame([], $result['errors'] ?? []);
|
||||
}
|
||||
|
||||
public function testUpdateFromAdminReturnsValidationErrorsForInvalidSessionPolicy(): void
|
||||
{
|
||||
$settingsSessionGateway = $this->createMock(SettingsSessionGateway::class);
|
||||
$settingsSessionGateway->method('getSessionIdleTimeoutMinutes')->willReturn(30);
|
||||
$settingsSessionGateway->method('getSessionAbsoluteTimeoutHours')->willReturn(8);
|
||||
$settingsSessionGateway->expects($this->once())
|
||||
->method('setSessionIdleTimeoutMinutes')
|
||||
->with(2)
|
||||
->willReturn(false);
|
||||
$settingsSessionGateway->expects($this->once())
|
||||
->method('setSessionAbsoluteTimeoutHours')
|
||||
->with(100)
|
||||
->willReturn(false);
|
||||
|
||||
$service = $this->newService($settingsSessionGateway);
|
||||
$result = $service->updateFromAdmin($this->validPostData([
|
||||
'session_idle_timeout_minutes' => '2',
|
||||
'session_absolute_timeout_hours' => '100',
|
||||
]));
|
||||
|
||||
$errors = is_array($result['errors'] ?? null) ? $result['errors'] : [];
|
||||
$errorKeys = [];
|
||||
foreach ($errors as $error) {
|
||||
if (is_array($error) && isset($error['key'])) {
|
||||
$errorKeys[] = (string) $error['key'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertContains('session_idle_timeout_invalid', $errorKeys);
|
||||
$this->assertContains('session_absolute_timeout_invalid', $errorKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $overrides
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function validPostData(array $overrides = []): array
|
||||
{
|
||||
$base = [
|
||||
'default_tenant_id' => '0',
|
||||
'default_role_id' => '0',
|
||||
'default_department_id' => '0',
|
||||
'app_title' => 'Demo',
|
||||
'app_locale' => 'de',
|
||||
'app_theme' => 'light',
|
||||
'app_theme_user' => '1',
|
||||
'app_registration' => '1',
|
||||
'api_token_default_ttl_days' => '90',
|
||||
'api_token_max_ttl_days' => '365',
|
||||
'user_inactivity_deactivate_days' => '180',
|
||||
'user_inactivity_delete_days' => '365',
|
||||
'session_idle_timeout_minutes' => '30',
|
||||
'session_absolute_timeout_hours' => '8',
|
||||
'api_cors_allowed_origins' => '',
|
||||
'system_audit_enabled' => '1',
|
||||
'system_audit_retention_days' => '365',
|
||||
'frontend_telemetry_enabled' => '1',
|
||||
'frontend_telemetry_sample_rate' => '0.2',
|
||||
'frontend_telemetry_allowed_events' => ['warn_once'],
|
||||
'app_primary_color' => '#2FA4A4',
|
||||
'smtp_host' => '',
|
||||
'smtp_port' => '',
|
||||
'smtp_user' => '',
|
||||
'smtp_password' => '',
|
||||
'smtp_secure' => '',
|
||||
'smtp_from' => '',
|
||||
'smtp_from_name' => '',
|
||||
'microsoft_shared_client_id' => '',
|
||||
'microsoft_shared_client_secret' => '',
|
||||
'microsoft_authority' => 'https://login.microsoftonline.com/common/v2.0',
|
||||
];
|
||||
|
||||
return array_replace($base, $overrides);
|
||||
}
|
||||
|
||||
private function newService(
|
||||
?SettingsSessionGateway $settingsSessionGateway = null,
|
||||
?SettingsMetadataGateway $settingsMetadataGateway = null
|
||||
): AdminSettingsService {
|
||||
$settingsDefaultsGateway = $this->createConfiguredMock(SettingsDefaultsGateway::class, [
|
||||
'getDefaultTenantId' => null,
|
||||
'getDefaultRoleId' => null,
|
||||
'getDefaultDepartmentId' => null,
|
||||
'setDefaultTenantId' => true,
|
||||
'setDefaultRoleId' => true,
|
||||
'setDefaultDepartmentId' => true,
|
||||
]);
|
||||
|
||||
$settingsAppGateway = $this->createConfiguredMock(SettingsAppGateway::class, [
|
||||
'getAppLocale' => 'de',
|
||||
'getAppTheme' => 'light',
|
||||
'isUserThemeAllowed' => true,
|
||||
'isRegistrationEnabled' => true,
|
||||
'getAppPrimaryColor' => '#2FA4A4',
|
||||
'setAppTitle' => true,
|
||||
'setAppLocale' => true,
|
||||
'setAppTheme' => true,
|
||||
'setUserThemeAllowed' => true,
|
||||
'setRegistrationEnabled' => true,
|
||||
'setAppPrimaryColor' => true,
|
||||
]);
|
||||
|
||||
$settingsApiPolicyGateway = $this->createConfiguredMock(SettingsApiPolicyGateway::class, [
|
||||
'getApiTokenDefaultTtlDays' => 90,
|
||||
'getApiTokenMaxTtlDays' => 365,
|
||||
'getApiCorsAllowedOriginsText' => '',
|
||||
'setApiTokenDefaultTtlDays' => true,
|
||||
'setApiTokenMaxTtlDays' => true,
|
||||
'setApiCorsAllowedOrigins' => true,
|
||||
]);
|
||||
|
||||
$settingsUserLifecycleGateway = $this->createConfiguredMock(SettingsUserLifecycleGateway::class, [
|
||||
'getUserInactivityDeactivateDays' => 180,
|
||||
'getUserInactivityDeleteDays' => 365,
|
||||
'setUserInactivityDeactivateDays' => true,
|
||||
'setUserInactivityDeleteDays' => true,
|
||||
]);
|
||||
|
||||
$settingsSessionGateway = $settingsSessionGateway ?? $this->createConfiguredMock(SettingsSessionGateway::class, [
|
||||
'getSessionIdleTimeoutMinutes' => 30,
|
||||
'getSessionAbsoluteTimeoutHours' => 8,
|
||||
'setSessionIdleTimeoutMinutes' => true,
|
||||
'setSessionAbsoluteTimeoutHours' => true,
|
||||
]);
|
||||
|
||||
$settingsSystemAuditGateway = $this->createConfiguredMock(SettingsSystemAuditGateway::class, [
|
||||
'isSystemAuditEnabled' => true,
|
||||
'getSystemAuditRetentionDays' => 365,
|
||||
'setSystemAuditEnabled' => true,
|
||||
'setSystemAuditRetentionDays' => true,
|
||||
]);
|
||||
|
||||
$settingsFrontendTelemetryGateway = $this->createConfiguredMock(SettingsFrontendTelemetryGateway::class, [
|
||||
'isFrontendTelemetryEnabled' => true,
|
||||
'getFrontendTelemetrySampleRate' => 0.2,
|
||||
'getFrontendTelemetryAllowedEvents' => ['warn_once'],
|
||||
'setFrontendTelemetryEnabled' => true,
|
||||
'setFrontendTelemetrySampleRate' => true,
|
||||
'setFrontendTelemetryAllowedEvents' => true,
|
||||
]);
|
||||
|
||||
$settingsMicrosoftGateway = $this->createConfiguredMock(SettingsMicrosoftGateway::class, [
|
||||
'getMicrosoftAuthority' => 'https://login.microsoftonline.com/common/v2.0',
|
||||
'setMicrosoftSharedClientId' => true,
|
||||
'setMicrosoftSharedClientSecret' => true,
|
||||
'setMicrosoftAuthority' => true,
|
||||
]);
|
||||
|
||||
$settingsSmtpGateway = $this->createConfiguredMock(SettingsSmtpGateway::class, [
|
||||
'getSmtpSecure' => '',
|
||||
'setSmtpHost' => true,
|
||||
'setSmtpPort' => true,
|
||||
'setSmtpUser' => true,
|
||||
'setSmtpPassword' => true,
|
||||
'setSmtpSecure' => true,
|
||||
'setSmtpFrom' => true,
|
||||
'setSmtpFromName' => true,
|
||||
]);
|
||||
|
||||
if ($settingsMetadataGateway === null) {
|
||||
$settingsMetadataGateway = $this->createMock(SettingsMetadataGateway::class);
|
||||
$settingsMetadataGateway->method('getValue')->willReturn(null);
|
||||
$settingsMetadataGateway->method('get')->willReturnCallback(
|
||||
static fn (string $key): array => ['key' => $key, 'description' => 'setting.default']
|
||||
);
|
||||
}
|
||||
|
||||
$settingCacheService = $this->createConfiguredMock(SettingCacheService::class, [
|
||||
'update' => true,
|
||||
]);
|
||||
|
||||
$tenantService = $this->createConfiguredMock(TenantService::class, [
|
||||
'list' => [],
|
||||
]);
|
||||
$roleService = $this->createConfiguredMock(RoleService::class, [
|
||||
'listActive' => [],
|
||||
]);
|
||||
$departmentService = $this->createConfiguredMock(DepartmentService::class, [
|
||||
'list' => [],
|
||||
]);
|
||||
|
||||
$rememberTokenRepository = $this->createConfiguredMock(RememberTokenRepository::class, [
|
||||
'countActive' => 0,
|
||||
]);
|
||||
$apiTokenRepository = $this->createConfiguredMock(ApiTokenRepository::class, [
|
||||
'countActive' => 0,
|
||||
]);
|
||||
$systemAuditService = $this->createConfiguredMock(SystemAuditService::class, [
|
||||
'record' => null,
|
||||
]);
|
||||
|
||||
return new AdminSettingsService(
|
||||
$settingsDefaultsGateway,
|
||||
$settingsAppGateway,
|
||||
$settingsApiPolicyGateway,
|
||||
$settingsUserLifecycleGateway,
|
||||
$settingsSessionGateway,
|
||||
$settingsSystemAuditGateway,
|
||||
$settingsFrontendTelemetryGateway,
|
||||
$settingsMicrosoftGateway,
|
||||
$settingsSmtpGateway,
|
||||
$settingsMetadataGateway,
|
||||
$settingCacheService,
|
||||
$tenantService,
|
||||
$roleService,
|
||||
$departmentService,
|
||||
$rememberTokenRepository,
|
||||
$apiTokenRepository,
|
||||
$systemAuditService
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user