diff --git a/i18n/default_de.json b/i18n/default_de.json index d88814e..3a786f4 100644 --- a/i18n/default_de.json +++ b/i18n/default_de.json @@ -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", diff --git a/i18n/default_en.json b/i18n/default_en.json index 7131ae0..09b91b8 100644 --- a/i18n/default_en.json +++ b/i18n/default_en.json @@ -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", diff --git a/lib/App/Container/Registrars/SettingsRegistrar.php b/lib/App/Container/Registrars/SettingsRegistrar.php index eac8f74..cf558e7 100644 --- a/lib/App/Container/Registrars/SettingsRegistrar.php +++ b/lib/App/Container/Registrars/SettingsRegistrar.php @@ -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), diff --git a/lib/Service/Settings/AdminSettingsService.php b/lib/Service/Settings/AdminSettingsService.php index c6e25c3..2c307d1 100644 --- a/lib/Service/Settings/AdminSettingsService.php +++ b/lib/Service/Settings/AdminSettingsService.php @@ -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(), diff --git a/pages/admin/settings/index(default).phtml b/pages/admin/settings/index(default).phtml index 74e66a2..aa8446f 100644 --- a/pages/admin/settings/index(default).phtml +++ b/pages/admin/settings/index(default).phtml @@ -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' : '';
+
+ +
+ +
+ + + +
+ + +
+
+