|false> Cache: tenantId → row or false (not found) */ private array $rowCache = []; public function __construct( private readonly HelpdeskTenantSettingsRepository $repository, private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway ) { } public function isOverrideEnabled(int $tenantId): bool { $row = $this->resolveRow($tenantId); if ($row === null) { return false; } return (int) ($row['override_enabled'] ?? 0) === 1; } /** * @return array|null Raw row data (secrets still encrypted) */ public function findByTenantId(int $tenantId): ?array { return $this->resolveRow($tenantId); } public function getAuthMode(int $tenantId): string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['auth_mode'] ?? '')); return $value === HelpdeskSettingsGateway::AUTH_MODE_OAUTH2 ? HelpdeskSettingsGateway::AUTH_MODE_OAUTH2 : HelpdeskSettingsGateway::AUTH_MODE_BASIC; } public function getODataBaseUrl(int $tenantId): string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['odata_base_url'] ?? '')); return $value !== '' ? rtrim($value, '/') : HelpdeskSettingsGateway::DEFAULT_ODATA_BASE_URL; } public function getCompanyName(int $tenantId): string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['company_name'] ?? '')); return $value !== '' ? $value : HelpdeskSettingsGateway::DEFAULT_COMPANY_NAME; } public function getBasicUser(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['basic_user'] ?? '')); return $value !== '' ? $value : null; } public function getBasicPassword(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $encrypted = trim((string) ($row['basic_password_enc'] ?? '')); if ($encrypted === '') { return null; } try { $decrypted = $this->settingsCryptoGateway->decryptString($encrypted); } catch (\Throwable) { return null; } $decrypted = trim($decrypted); return $decrypted !== '' ? $decrypted : null; } public function getOAuthTenantId(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['oauth_tenant_id'] ?? '')); return $value !== '' ? $value : null; } public function getOAuthClientId(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['oauth_client_id'] ?? '')); return $value !== '' ? $value : null; } public function getOAuthClientSecret(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $encrypted = trim((string) ($row['oauth_client_secret_enc'] ?? '')); if ($encrypted === '') { return null; } try { $decrypted = $this->settingsCryptoGateway->decryptString($encrypted); } catch (\Throwable) { return null; } $decrypted = trim($decrypted); return $decrypted !== '' ? $decrypted : null; } public function getOAuthTokenEndpoint(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['oauth_token_endpoint'] ?? '')); return $value !== '' ? $value : null; } public function getSystemRecommendationsConfig(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['system_recommendations_config'] ?? '')); return $value !== '' ? $value : null; } public function getControllingRiskConfig(int $tenantId): ?string { $row = $this->resolveRow($tenantId); $value = trim((string) ($row['controlling_risk_config'] ?? '')); return $value !== '' ? $value : null; } /** * Persist tenant override settings. Secrets are encrypted before storage. * * @param array $data Field values (plain-text passwords/secrets accepted) */ public function save(int $tenantId, array $data): bool { if ($tenantId <= 0) { return false; } // Encrypt secrets before storage if (isset($data['basic_password']) && is_string($data['basic_password'])) { $password = trim($data['basic_password']); unset($data['basic_password']); if ($password !== '') { try { $data['basic_password_enc'] = $this->settingsCryptoGateway->encryptString($password); } catch (\Throwable) { return false; } } } if (isset($data['oauth_client_secret']) && is_string($data['oauth_client_secret'])) { $secret = trim($data['oauth_client_secret']); unset($data['oauth_client_secret']); if ($secret !== '') { try { $data['oauth_client_secret_enc'] = $this->settingsCryptoGateway->encryptString($secret); } catch (\Throwable) { return false; } } } // Invalidate cache after write unset($this->rowCache[$tenantId]); return $this->repository->upsert($tenantId, $data); } public function deleteByTenantId(int $tenantId): bool { unset($this->rowCache[$tenantId]); return $this->repository->deleteByTenantId($tenantId); } /** * Load row once per tenant per request. Returns null if no row exists. * * @return array|null */ private function resolveRow(int $tenantId): ?array { if ($tenantId <= 0) { return null; } if (array_key_exists($tenantId, $this->rowCache)) { $cached = $this->rowCache[$tenantId]; return $cached === false ? null : $cached; } $row = $this->repository->findByTenantId($tenantId); $this->rowCache[$tenantId] = $row ?? false; return $row; } }