2026-04-06 10:29:59 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MintyPHP\Module\Helpdesk\Service;
|
|
|
|
|
|
2026-04-13 19:28:07 +02:00
|
|
|
use MintyPHP\Module\Helpdesk\Repository\HelpdeskTenantSettingsRepository;
|
2026-04-06 10:29:59 +02:00
|
|
|
use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gateway for per-tenant helpdesk BC connection settings.
|
|
|
|
|
*
|
|
|
|
|
* Wraps the tenant settings repository and handles encryption/decryption
|
|
|
|
|
* of secrets (basic_password, oauth_client_secret) via SettingsCryptoGateway.
|
2026-04-06 22:22:17 +02:00
|
|
|
*
|
|
|
|
|
* Uses a per-request row cache to avoid N+1 queries when multiple getters
|
|
|
|
|
* are called for the same tenant (e.g. during settings resolution).
|
2026-04-06 10:29:59 +02:00
|
|
|
*/
|
|
|
|
|
class HelpdeskTenantSettingsGateway
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
/** @var array<int, array<string, mixed>|false> Cache: tenantId → row or false (not found) */
|
|
|
|
|
private array $rowCache = [];
|
|
|
|
|
|
2026-04-06 10:29:59 +02:00
|
|
|
public function __construct(
|
|
|
|
|
private readonly HelpdeskTenantSettingsRepository $repository,
|
|
|
|
|
private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway
|
|
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isOverrideEnabled(int $tenantId): bool
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
if ($row === null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (int) ($row['override_enabled'] ?? 0) === 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array<string, mixed>|null Raw row data (secrets still encrypted)
|
|
|
|
|
*/
|
|
|
|
|
public function findByTenantId(int $tenantId): ?array
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
return $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getAuthMode(int $tenantId): string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$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
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['odata_base_url'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? rtrim($value, '/') : HelpdeskSettingsGateway::DEFAULT_ODATA_BASE_URL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getCompanyName(int $tenantId): string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['company_name'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? $value : HelpdeskSettingsGateway::DEFAULT_COMPANY_NAME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getBasicUser(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['basic_user'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? $value : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getBasicPassword(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$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
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['oauth_tenant_id'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? $value : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getOAuthClientId(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['oauth_client_id'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? $value : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getOAuthClientSecret(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$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
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
2026-04-06 10:29:59 +02:00
|
|
|
$value = trim((string) ($row['oauth_token_endpoint'] ?? ''));
|
|
|
|
|
|
|
|
|
|
return $value !== '' ? $value : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getSystemRecommendationsConfig(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
|
|
|
|
$value = trim((string) ($row['system_recommendations_config'] ?? ''));
|
2026-04-06 10:29:59 +02:00
|
|
|
|
2026-04-06 22:22:17 +02:00
|
|
|
return $value !== '' ? $value : null;
|
2026-04-06 10:29:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getControllingRiskConfig(int $tenantId): ?string
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
$row = $this->resolveRow($tenantId);
|
|
|
|
|
$value = trim((string) ($row['controlling_risk_config'] ?? ''));
|
2026-04-06 10:29:59 +02:00
|
|
|
|
2026-04-06 22:22:17 +02:00
|
|
|
return $value !== '' ? $value : null;
|
2026-04-06 10:29:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Persist tenant override settings. Secrets are encrypted before storage.
|
|
|
|
|
*
|
|
|
|
|
* @param array<string, mixed> $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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 22:22:17 +02:00
|
|
|
// Invalidate cache after write
|
|
|
|
|
unset($this->rowCache[$tenantId]);
|
|
|
|
|
|
2026-04-06 10:29:59 +02:00
|
|
|
return $this->repository->upsert($tenantId, $data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function deleteByTenantId(int $tenantId): bool
|
|
|
|
|
{
|
2026-04-06 22:22:17 +02:00
|
|
|
unset($this->rowCache[$tenantId]);
|
|
|
|
|
|
2026-04-06 10:29:59 +02:00
|
|
|
return $this->repository->deleteByTenantId($tenantId);
|
|
|
|
|
}
|
2026-04-06 22:22:17 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load row once per tenant per request. Returns null if no row exists.
|
|
|
|
|
*
|
|
|
|
|
* @return array<string, mixed>|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;
|
|
|
|
|
}
|
2026-04-06 10:29:59 +02:00
|
|
|
}
|