|
|
|
|
@@ -124,42 +124,81 @@ class AdminSettingsService
|
|
|
|
|
|
|
|
|
|
public function updateFromAdmin(array $post): array
|
|
|
|
|
{
|
|
|
|
|
$defaultTenantId = (int) ($post['default_tenant_id'] ?? 0);
|
|
|
|
|
$defaultRoleId = (int) ($post['default_role_id'] ?? 0);
|
|
|
|
|
$defaultDepartmentId = (int) ($post['default_department_id'] ?? 0);
|
|
|
|
|
$appTitle = trim((string) ($post['app_title'] ?? ''));
|
|
|
|
|
$appLocale = trim((string) ($post['app_locale'] ?? ''));
|
|
|
|
|
$appTheme = trim((string) ($post['app_theme'] ?? ''));
|
|
|
|
|
$appThemeUser = isset($post['app_theme_user']);
|
|
|
|
|
$appRegistration = isset($post['app_registration']);
|
|
|
|
|
$apiTokenDefaultTtlDays = (int) ($post['api_token_default_ttl_days'] ?? 0);
|
|
|
|
|
$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);
|
|
|
|
|
$microsoftAutoRememberDefault = isset($post['microsoft_auto_remember_default']);
|
|
|
|
|
$rememberTokenLifetimeDays = (int) ($post['remember_token_lifetime_days'] ?? 0);
|
|
|
|
|
$apiCorsAllowedOrigins = $this->normalizeScalarText($post['api_cors_allowed_origins'] ?? '');
|
|
|
|
|
$systemAuditEnabled = isset($post['system_audit_enabled']);
|
|
|
|
|
$systemAuditRetentionDays = (int) ($post['system_audit_retention_days'] ?? 0);
|
|
|
|
|
$frontendTelemetryEnabled = isset($post['frontend_telemetry_enabled']);
|
|
|
|
|
$frontendTelemetrySampleRate = $this->normalizeScalarText($post['frontend_telemetry_sample_rate'] ?? '');
|
|
|
|
|
$frontendTelemetryAllowedEvents = $post['frontend_telemetry_allowed_events'] ?? [];
|
|
|
|
|
$appPrimaryColor = $this->normalizePrimaryColor($post['app_primary_color'] ?? '');
|
|
|
|
|
$smtpHost = trim((string) ($post['smtp_host'] ?? ''));
|
|
|
|
|
$smtpPort = (int) ($post['smtp_port'] ?? 0);
|
|
|
|
|
$smtpUser = trim((string) ($post['smtp_user'] ?? ''));
|
|
|
|
|
$smtpPassword = (string) ($post['smtp_password'] ?? '');
|
|
|
|
|
$smtpSecure = trim((string) ($post['smtp_secure'] ?? ''));
|
|
|
|
|
$smtpFrom = trim((string) ($post['smtp_from'] ?? ''));
|
|
|
|
|
$smtpFromName = trim((string) ($post['smtp_from_name'] ?? ''));
|
|
|
|
|
$microsoftSharedClientId = trim((string) ($post['microsoft_shared_client_id'] ?? ''));
|
|
|
|
|
$microsoftSharedClientSecret = (string) ($post['microsoft_shared_client_secret'] ?? '');
|
|
|
|
|
$microsoftAuthority = trim((string) ($post['microsoft_authority'] ?? ''));
|
|
|
|
|
|
|
|
|
|
$input = $this->sanitizeAdminInput($post);
|
|
|
|
|
$beforeAudit = $this->captureAuditSnapshot();
|
|
|
|
|
$errors = [];
|
|
|
|
|
$beforeAudit = [
|
|
|
|
|
|
|
|
|
|
$this->applyDefaultsAndAppSettings($input);
|
|
|
|
|
array_push($errors, ...$this->applyApiAndLifecycleSettings($input));
|
|
|
|
|
array_push($errors, ...$this->applySessionAndLoginSettings($input));
|
|
|
|
|
array_push($errors, ...$this->applyAuditAndTelemetrySettings($input));
|
|
|
|
|
array_push($errors, ...$this->applyAppPrimaryColor($input));
|
|
|
|
|
$this->applySmtpSettings($input);
|
|
|
|
|
array_push($errors, ...$this->applyMicrosoftSettings($input));
|
|
|
|
|
$this->updateSettingsCache($input);
|
|
|
|
|
|
|
|
|
|
$afterAudit = $this->captureAuditSnapshot();
|
|
|
|
|
$this->recordAudit($errors, $beforeAudit, $afterAudit);
|
|
|
|
|
|
|
|
|
|
return ['errors' => $errors];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeAdminInput(array $post): array
|
|
|
|
|
{
|
|
|
|
|
return array_merge(
|
|
|
|
|
$this->sanitizeAppAndPolicyInput($post),
|
|
|
|
|
$this->sanitizeIntegrationInput($post),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeAppAndPolicyInput(array $post): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'default_tenant_id' => (int) ($post['default_tenant_id'] ?? 0),
|
|
|
|
|
'default_role_id' => (int) ($post['default_role_id'] ?? 0),
|
|
|
|
|
'default_department_id' => (int) ($post['default_department_id'] ?? 0),
|
|
|
|
|
'app_title' => trim((string) ($post['app_title'] ?? '')),
|
|
|
|
|
'app_locale' => trim((string) ($post['app_locale'] ?? '')),
|
|
|
|
|
'app_theme' => trim((string) ($post['app_theme'] ?? '')),
|
|
|
|
|
'app_theme_user' => isset($post['app_theme_user']),
|
|
|
|
|
'app_registration' => isset($post['app_registration']),
|
|
|
|
|
'app_primary_color' => $this->normalizePrimaryColor($post['app_primary_color'] ?? ''),
|
|
|
|
|
'api_token_default_ttl_days' => (int) ($post['api_token_default_ttl_days'] ?? 0),
|
|
|
|
|
'api_token_max_ttl_days' => (int) ($post['api_token_max_ttl_days'] ?? 0),
|
|
|
|
|
'api_cors_allowed_origins' => $this->normalizeScalarText($post['api_cors_allowed_origins'] ?? ''),
|
|
|
|
|
'user_inactivity_deactivate_days' => (int) ($post['user_inactivity_deactivate_days'] ?? 0),
|
|
|
|
|
'user_inactivity_delete_days' => (int) ($post['user_inactivity_delete_days'] ?? 0),
|
|
|
|
|
'session_idle_timeout_minutes' => (int) ($post['session_idle_timeout_minutes'] ?? 0),
|
|
|
|
|
'session_absolute_timeout_hours' => (int) ($post['session_absolute_timeout_hours'] ?? 0),
|
|
|
|
|
'microsoft_auto_remember_default' => isset($post['microsoft_auto_remember_default']),
|
|
|
|
|
'remember_token_lifetime_days' => (int) ($post['remember_token_lifetime_days'] ?? 0),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeIntegrationInput(array $post): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'system_audit_enabled' => isset($post['system_audit_enabled']),
|
|
|
|
|
'system_audit_retention_days' => (int) ($post['system_audit_retention_days'] ?? 0),
|
|
|
|
|
'frontend_telemetry_enabled' => isset($post['frontend_telemetry_enabled']),
|
|
|
|
|
'frontend_telemetry_sample_rate' => $this->normalizeScalarText($post['frontend_telemetry_sample_rate'] ?? ''),
|
|
|
|
|
'frontend_telemetry_allowed_events' => $post['frontend_telemetry_allowed_events'] ?? [],
|
|
|
|
|
'smtp_host' => trim((string) ($post['smtp_host'] ?? '')),
|
|
|
|
|
'smtp_port' => (int) ($post['smtp_port'] ?? 0),
|
|
|
|
|
'smtp_user' => trim((string) ($post['smtp_user'] ?? '')),
|
|
|
|
|
'smtp_password' => (string) ($post['smtp_password'] ?? ''),
|
|
|
|
|
'smtp_secure' => trim((string) ($post['smtp_secure'] ?? '')),
|
|
|
|
|
'smtp_from' => trim((string) ($post['smtp_from'] ?? '')),
|
|
|
|
|
'smtp_from_name' => trim((string) ($post['smtp_from_name'] ?? '')),
|
|
|
|
|
'microsoft_shared_client_id' => trim((string) ($post['microsoft_shared_client_id'] ?? '')),
|
|
|
|
|
'microsoft_shared_client_secret' => (string) ($post['microsoft_shared_client_secret'] ?? ''),
|
|
|
|
|
'microsoft_authority' => trim((string) ($post['microsoft_authority'] ?? '')),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function captureAuditSnapshot(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'default_tenant_id' => $this->settingsDefaultsGateway->getDefaultTenantId(),
|
|
|
|
|
'default_role_id' => $this->settingsDefaultsGateway->getDefaultRoleId(),
|
|
|
|
|
'default_department_id' => $this->settingsDefaultsGateway->getDefaultDepartmentId(),
|
|
|
|
|
@@ -182,155 +221,153 @@ class AdminSettingsService
|
|
|
|
|
'frontend_telemetry_sample_rate' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
|
|
|
|
|
'frontend_telemetry_allowed_events' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents(),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultTenantId($defaultTenantId > 0 ? $defaultTenantId : null);
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultRoleId($defaultRoleId > 0 ? $defaultRoleId : null);
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultDepartmentId($defaultDepartmentId > 0 ? $defaultDepartmentId : null);
|
|
|
|
|
$this->settingsAppGateway->setAppTitle($appTitle);
|
|
|
|
|
$this->settingsAppGateway->setAppLocale($appLocale);
|
|
|
|
|
$this->settingsAppGateway->setAppTheme($appTheme);
|
|
|
|
|
$this->settingsAppGateway->setUserThemeAllowed($appThemeUser);
|
|
|
|
|
$this->settingsAppGateway->setRegistrationEnabled($appRegistration);
|
|
|
|
|
private function applyDefaultsAndAppSettings(array $input): void
|
|
|
|
|
{
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultTenantId($input['default_tenant_id'] > 0 ? $input['default_tenant_id'] : null);
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultRoleId($input['default_role_id'] > 0 ? $input['default_role_id'] : null);
|
|
|
|
|
$this->settingsDefaultsGateway->setDefaultDepartmentId($input['default_department_id'] > 0 ? $input['default_department_id'] : null);
|
|
|
|
|
$this->settingsAppGateway->setAppTitle($input['app_title']);
|
|
|
|
|
$this->settingsAppGateway->setAppLocale($input['app_locale']);
|
|
|
|
|
$this->settingsAppGateway->setAppTheme($input['app_theme']);
|
|
|
|
|
$this->settingsAppGateway->setUserThemeAllowed($input['app_theme_user']);
|
|
|
|
|
$this->settingsAppGateway->setRegistrationEnabled($input['app_registration']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$apiTokenMaxTtlOk = $this->settingsApiPolicyGateway->setApiTokenMaxTtlDays($apiTokenMaxTtlDays > 0 ? $apiTokenMaxTtlDays : null);
|
|
|
|
|
if (!$apiTokenMaxTtlOk) {
|
|
|
|
|
/** @return list<array{message: string, key: string}> */
|
|
|
|
|
private function applyApiAndLifecycleSettings(array $input): array
|
|
|
|
|
{
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
if (!$this->settingsApiPolicyGateway->setApiTokenMaxTtlDays($input['api_token_max_ttl_days'] > 0 ? $input['api_token_max_ttl_days'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'API token max lifetime is invalid', 'key' => 'api_token_max_ttl_invalid'];
|
|
|
|
|
}
|
|
|
|
|
$apiTokenTtlOk = $this->settingsApiPolicyGateway->setApiTokenDefaultTtlDays($apiTokenDefaultTtlDays > 0 ? $apiTokenDefaultTtlDays : null);
|
|
|
|
|
if (!$apiTokenTtlOk) {
|
|
|
|
|
if (!$this->settingsApiPolicyGateway->setApiTokenDefaultTtlDays($input['api_token_default_ttl_days'] > 0 ? $input['api_token_default_ttl_days'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'API token default lifetime is invalid', 'key' => 'api_token_default_ttl_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$userDeactivateOk = $this->settingsUserLifecycleGateway->setUserInactivityDeactivateDays($userInactivityDeactivateDays);
|
|
|
|
|
if (!$userDeactivateOk) {
|
|
|
|
|
$errors[] = [
|
|
|
|
|
'message' => 'User inactivity deactivation period is invalid',
|
|
|
|
|
'key' => 'user_inactivity_deactivate_days_invalid',
|
|
|
|
|
];
|
|
|
|
|
if (!$this->settingsApiPolicyGateway->setApiCorsAllowedOrigins($input['api_cors_allowed_origins'])) {
|
|
|
|
|
$errors[] = ['message' => 'CORS allowed origins are invalid', 'key' => 'api_cors_allowed_origins_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($userInactivityDeactivateDays > 0) {
|
|
|
|
|
$userDeleteOk = $this->settingsUserLifecycleGateway->setUserInactivityDeleteDays($userInactivityDeleteDays);
|
|
|
|
|
if (!$userDeleteOk) {
|
|
|
|
|
$errors[] = [
|
|
|
|
|
'message' => 'User inactivity deletion period is invalid',
|
|
|
|
|
'key' => 'user_inactivity_delete_days_invalid',
|
|
|
|
|
];
|
|
|
|
|
if (!$this->settingsUserLifecycleGateway->setUserInactivityDeactivateDays($input['user_inactivity_deactivate_days'])) {
|
|
|
|
|
$errors[] = ['message' => 'User inactivity deactivation period is invalid', 'key' => 'user_inactivity_deactivate_days_invalid'];
|
|
|
|
|
}
|
|
|
|
|
if ($input['user_inactivity_deactivate_days'] > 0) {
|
|
|
|
|
if (!$this->settingsUserLifecycleGateway->setUserInactivityDeleteDays($input['user_inactivity_delete_days'])) {
|
|
|
|
|
$errors[] = ['message' => 'User inactivity deletion period is invalid', 'key' => 'user_inactivity_delete_days_invalid'];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($userInactivityDeleteDays > 0) {
|
|
|
|
|
$errors[] = [
|
|
|
|
|
'message' => 'Auto-delete is ignored while auto-deactivate is disabled',
|
|
|
|
|
'key' => 'user_inactivity_delete_requires_deactivate',
|
|
|
|
|
];
|
|
|
|
|
if ($input['user_inactivity_delete_days'] > 0) {
|
|
|
|
|
$errors[] = ['message' => 'Auto-delete is ignored while auto-deactivate is disabled', 'key' => 'user_inactivity_delete_requires_deactivate'];
|
|
|
|
|
}
|
|
|
|
|
$this->settingsUserLifecycleGateway->setUserInactivityDeleteDays(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$sessionIdleOk = $this->settingsSessionGateway->setSessionIdleTimeoutMinutes(
|
|
|
|
|
$sessionIdleTimeoutMinutes > 0 ? $sessionIdleTimeoutMinutes : null
|
|
|
|
|
);
|
|
|
|
|
if (!$sessionIdleOk) {
|
|
|
|
|
return $errors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return list<array{message: string, key: string}> */
|
|
|
|
|
private function applySessionAndLoginSettings(array $input): array
|
|
|
|
|
{
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
if (!$this->settingsSessionGateway->setSessionIdleTimeoutMinutes($input['session_idle_timeout_minutes'] > 0 ? $input['session_idle_timeout_minutes'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'Session idle timeout is invalid', 'key' => 'session_idle_timeout_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$sessionAbsoluteOk = $this->settingsSessionGateway->setSessionAbsoluteTimeoutHours(
|
|
|
|
|
$sessionAbsoluteTimeoutHours > 0 ? $sessionAbsoluteTimeoutHours : null
|
|
|
|
|
);
|
|
|
|
|
if (!$sessionAbsoluteOk) {
|
|
|
|
|
if (!$this->settingsSessionGateway->setSessionAbsoluteTimeoutHours($input['session_absolute_timeout_hours'] > 0 ? $input['session_absolute_timeout_hours'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'Session absolute timeout is invalid', 'key' => 'session_absolute_timeout_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->settingsLoginPersistenceGateway->setMicrosoftAutoRememberDefault($microsoftAutoRememberDefault);
|
|
|
|
|
|
|
|
|
|
$rememberTtlOk = $this->settingsLoginPersistenceGateway->setRememberTokenLifetimeDays(
|
|
|
|
|
$rememberTokenLifetimeDays > 0 ? $rememberTokenLifetimeDays : null
|
|
|
|
|
);
|
|
|
|
|
if (!$rememberTtlOk) {
|
|
|
|
|
$this->settingsLoginPersistenceGateway->setMicrosoftAutoRememberDefault($input['microsoft_auto_remember_default']);
|
|
|
|
|
if (!$this->settingsLoginPersistenceGateway->setRememberTokenLifetimeDays($input['remember_token_lifetime_days'] > 0 ? $input['remember_token_lifetime_days'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'Remember token lifetime is invalid', 'key' => 'remember_token_lifetime_days_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$apiCorsOriginsOk = $this->settingsApiPolicyGateway->setApiCorsAllowedOrigins($apiCorsAllowedOrigins);
|
|
|
|
|
if (!$apiCorsOriginsOk) {
|
|
|
|
|
$errors[] = ['message' => 'CORS allowed origins are invalid', 'key' => 'api_cors_allowed_origins_invalid'];
|
|
|
|
|
}
|
|
|
|
|
return $errors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$systemAuditEnabledOk = $this->settingsSystemAuditGateway->setSystemAuditEnabled($systemAuditEnabled);
|
|
|
|
|
if (!$systemAuditEnabledOk) {
|
|
|
|
|
/** @return list<array{message: string, key: string}> */
|
|
|
|
|
private function applyAuditAndTelemetrySettings(array $input): array
|
|
|
|
|
{
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
if (!$this->settingsSystemAuditGateway->setSystemAuditEnabled($input['system_audit_enabled'])) {
|
|
|
|
|
$errors[] = ['message' => 'System audit setting is invalid', 'key' => 'system_audit_enabled_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$systemAuditRetentionOk = $this->settingsSystemAuditGateway->setSystemAuditRetentionDays(
|
|
|
|
|
$systemAuditRetentionDays > 0 ? $systemAuditRetentionDays : null
|
|
|
|
|
);
|
|
|
|
|
if (!$systemAuditRetentionOk) {
|
|
|
|
|
if (!$this->settingsSystemAuditGateway->setSystemAuditRetentionDays($input['system_audit_retention_days'] > 0 ? $input['system_audit_retention_days'] : null)) {
|
|
|
|
|
$errors[] = ['message' => 'System audit retention is invalid', 'key' => 'system_audit_retention_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$frontendTelemetryEnabledOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetryEnabled($frontendTelemetryEnabled);
|
|
|
|
|
if (!$frontendTelemetryEnabledOk) {
|
|
|
|
|
if (!$this->settingsFrontendTelemetryGateway->setFrontendTelemetryEnabled($input['frontend_telemetry_enabled'])) {
|
|
|
|
|
$errors[] = ['message' => 'Frontend telemetry enabled setting is invalid', 'key' => 'frontend_telemetry_enabled_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$frontendTelemetryRateValue = null;
|
|
|
|
|
if ($frontendTelemetrySampleRate !== '') {
|
|
|
|
|
$frontendTelemetryRateValue = is_numeric($frontendTelemetrySampleRate)
|
|
|
|
|
? (float) $frontendTelemetrySampleRate
|
|
|
|
|
$rateValue = null;
|
|
|
|
|
if ($input['frontend_telemetry_sample_rate'] !== '') {
|
|
|
|
|
$rateValue = is_numeric($input['frontend_telemetry_sample_rate'])
|
|
|
|
|
? (float) $input['frontend_telemetry_sample_rate']
|
|
|
|
|
: NAN;
|
|
|
|
|
}
|
|
|
|
|
$frontendTelemetrySampleRateOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetrySampleRate($frontendTelemetryRateValue);
|
|
|
|
|
if (!$frontendTelemetrySampleRateOk) {
|
|
|
|
|
if (!$this->settingsFrontendTelemetryGateway->setFrontendTelemetrySampleRate($rateValue)) {
|
|
|
|
|
$errors[] = ['message' => 'Frontend telemetry sample rate is invalid', 'key' => 'frontend_telemetry_sample_rate_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$frontendTelemetryAllowedEventsOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetryAllowedEvents($frontendTelemetryAllowedEvents);
|
|
|
|
|
if (!$frontendTelemetryAllowedEventsOk) {
|
|
|
|
|
if (!$this->settingsFrontendTelemetryGateway->setFrontendTelemetryAllowedEvents($input['frontend_telemetry_allowed_events'])) {
|
|
|
|
|
$errors[] = ['message' => 'Frontend telemetry allowed events are invalid', 'key' => 'frontend_telemetry_allowed_events_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$primaryOk = $this->settingsAppGateway->setAppPrimaryColor($appPrimaryColor);
|
|
|
|
|
if (!$primaryOk) {
|
|
|
|
|
$appPrimaryColor = '';
|
|
|
|
|
$errors[] = ['message' => 'Primary color is invalid', 'key' => 'app_primary_invalid'];
|
|
|
|
|
}
|
|
|
|
|
return $errors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpHost($smtpHost);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpPort($smtpPort > 0 ? $smtpPort : null);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpUser($smtpUser);
|
|
|
|
|
if (trim($smtpPassword) !== '') {
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpPassword($smtpPassword);
|
|
|
|
|
/** @return list<array{message: string, key: string}> */
|
|
|
|
|
private function applyAppPrimaryColor(array &$input): array
|
|
|
|
|
{
|
|
|
|
|
if (!$this->settingsAppGateway->setAppPrimaryColor($input['app_primary_color'])) {
|
|
|
|
|
$input['app_primary_color'] = '';
|
|
|
|
|
return [['message' => 'Primary color is invalid', 'key' => 'app_primary_invalid']];
|
|
|
|
|
}
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpSecure($smtpSecure);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpFrom($smtpFrom);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpFromName($smtpFromName);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$msClientIdOk = $this->settingsMicrosoftGateway->setMicrosoftSharedClientId($microsoftSharedClientId);
|
|
|
|
|
if (!$msClientIdOk) {
|
|
|
|
|
private function applySmtpSettings(array $input): void
|
|
|
|
|
{
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpHost($input['smtp_host']);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpPort($input['smtp_port'] > 0 ? $input['smtp_port'] : null);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpUser($input['smtp_user']);
|
|
|
|
|
if (trim($input['smtp_password']) !== '') {
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpPassword($input['smtp_password']);
|
|
|
|
|
}
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpSecure($input['smtp_secure']);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpFrom($input['smtp_from']);
|
|
|
|
|
$this->settingsSmtpGateway->setSmtpFromName($input['smtp_from_name']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return list<array{message: string, key: string}> */
|
|
|
|
|
private function applyMicrosoftSettings(array $input): array
|
|
|
|
|
{
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
if (!$this->settingsMicrosoftGateway->setMicrosoftSharedClientId($input['microsoft_shared_client_id'])) {
|
|
|
|
|
$errors[] = ['message' => 'Microsoft client ID is invalid', 'key' => 'microsoft_client_id_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$msAuthorityOk = $this->settingsMicrosoftGateway->setMicrosoftAuthority($microsoftAuthority);
|
|
|
|
|
if (!$msAuthorityOk) {
|
|
|
|
|
if (!$this->settingsMicrosoftGateway->setMicrosoftAuthority($input['microsoft_authority'])) {
|
|
|
|
|
$errors[] = ['message' => 'Microsoft authority is invalid', 'key' => 'microsoft_authority_invalid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trim($microsoftSharedClientSecret) !== '') {
|
|
|
|
|
$msSecretOk = $this->settingsMicrosoftGateway->setMicrosoftSharedClientSecret($microsoftSharedClientSecret);
|
|
|
|
|
if (!$msSecretOk) {
|
|
|
|
|
$errors[] = [
|
|
|
|
|
'message' => 'Microsoft client secret could not be encrypted',
|
|
|
|
|
'key' => 'microsoft_client_secret_invalid',
|
|
|
|
|
];
|
|
|
|
|
if (trim($input['microsoft_shared_client_secret']) !== '') {
|
|
|
|
|
if (!$this->settingsMicrosoftGateway->setMicrosoftSharedClientSecret($input['microsoft_shared_client_secret'])) {
|
|
|
|
|
$errors[] = ['message' => 'Microsoft client secret could not be encrypted', 'key' => 'microsoft_client_secret_invalid'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $errors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function updateSettingsCache(array $input): void
|
|
|
|
|
{
|
|
|
|
|
$this->settingCacheService->update([
|
|
|
|
|
'app_title' => $appTitle !== '' ? $appTitle : null,
|
|
|
|
|
'app_locale' => $appLocale !== '' ? $appLocale : null,
|
|
|
|
|
'app_theme' => $appTheme !== '' ? $appTheme : null,
|
|
|
|
|
'app_theme_user' => $appThemeUser ? '1' : '0',
|
|
|
|
|
'app_registration' => $appRegistration ? '1' : '0',
|
|
|
|
|
'app_primary_color' => $appPrimaryColor !== '' ? $appPrimaryColor : null,
|
|
|
|
|
'app_title' => $input['app_title'] !== '' ? $input['app_title'] : null,
|
|
|
|
|
'app_locale' => $input['app_locale'] !== '' ? $input['app_locale'] : null,
|
|
|
|
|
'app_theme' => $input['app_theme'] !== '' ? $input['app_theme'] : null,
|
|
|
|
|
'app_theme_user' => $input['app_theme_user'] ? '1' : '0',
|
|
|
|
|
'app_registration' => $input['app_registration'] ? '1' : '0',
|
|
|
|
|
'app_primary_color' => $input['app_primary_color'] !== '' ? $input['app_primary_color'] : null,
|
|
|
|
|
'api_token_default_ttl_days' => (string) $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
|
|
|
|
|
'api_token_max_ttl_days' => (string) $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
|
|
|
|
|
'api_cors_allowed_origins' => $this->settingsMetadataGateway->getValue(SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY),
|
|
|
|
|
@@ -338,39 +375,17 @@ class AdminSettingsService
|
|
|
|
|
'frontend_telemetry_sample_rate' => (string) $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
|
|
|
|
|
'frontend_telemetry_allowed_events' => implode(',', $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents()),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$afterAudit = [
|
|
|
|
|
'default_tenant_id' => $this->settingsDefaultsGateway->getDefaultTenantId(),
|
|
|
|
|
'default_role_id' => $this->settingsDefaultsGateway->getDefaultRoleId(),
|
|
|
|
|
'default_department_id' => $this->settingsDefaultsGateway->getDefaultDepartmentId(),
|
|
|
|
|
'app_locale' => $this->settingsAppGateway->getAppLocale(),
|
|
|
|
|
'app_theme' => $this->settingsAppGateway->getAppTheme(),
|
|
|
|
|
'app_theme_user' => $this->settingsAppGateway->isUserThemeAllowed(),
|
|
|
|
|
'app_registration' => $this->settingsAppGateway->isRegistrationEnabled(),
|
|
|
|
|
'app_primary_color' => $this->settingsAppGateway->getAppPrimaryColor(),
|
|
|
|
|
'api_token_default_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
|
|
|
|
|
'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(),
|
|
|
|
|
'microsoft_auto_remember_default' => $this->settingsLoginPersistenceGateway->isMicrosoftAutoRememberDefault(),
|
|
|
|
|
'remember_token_lifetime_days' => $this->settingsLoginPersistenceGateway->getRememberTokenLifetimeDays(),
|
|
|
|
|
'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
|
|
|
|
|
'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
|
|
|
|
|
'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
|
|
|
|
|
'frontend_telemetry_sample_rate' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
|
|
|
|
|
'frontend_telemetry_allowed_events' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/** @param list<array{message: string, key: string}> $errors */
|
|
|
|
|
private function recordAudit(array $errors, array $beforeAudit, array $afterAudit): void
|
|
|
|
|
{
|
|
|
|
|
$outcome = $errors === [] ? 'success' : 'failed';
|
|
|
|
|
$errorKeys = [];
|
|
|
|
|
if ($errors !== []) {
|
|
|
|
|
foreach ($errors as $error) {
|
|
|
|
|
$key = trim((string) $error['key']);
|
|
|
|
|
if ($key !== '') {
|
|
|
|
|
$errorKeys[] = $key;
|
|
|
|
|
}
|
|
|
|
|
foreach ($errors as $error) {
|
|
|
|
|
$key = trim((string) $error['key']);
|
|
|
|
|
if ($key !== '') {
|
|
|
|
|
$errorKeys[] = $key;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -380,8 +395,6 @@ class AdminSettingsService
|
|
|
|
|
'error_code' => $errors === [] ? null : 'validation_error',
|
|
|
|
|
'metadata' => $errors === [] ? [] : ['error_keys' => $errorKeys],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return ['errors' => $errors];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function normalizeScalarText(mixed $value): string
|
|
|
|
|
|