Restructure helpdesk tenant settings into per-connection config with improved token handling. Update risk radar data endpoint and UI. Clean up ImageUploadTrait and update architecture contract tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
3.0 KiB
PHP
105 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Module\Helpdesk\Service;
|
|
|
|
use MintyPHP\DB;
|
|
use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface;
|
|
|
|
/**
|
|
* Repository for OAuth2 token cache (helpdesk_oauth_token_cache table).
|
|
*
|
|
* All tokens are stored encrypted and tenant-scoped.
|
|
* This is part of the OAuth2 skeleton for V1.
|
|
*/
|
|
class HelpdeskTokenRepository
|
|
{
|
|
public function __construct(
|
|
private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Find a valid (non-expired) cached token for the given tenant.
|
|
*/
|
|
public function findValidToken(int $tenantId): ?string
|
|
{
|
|
if ($tenantId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$result = DB::selectOne(
|
|
'SELECT access_token_encrypted FROM helpdesk_oauth_token_cache WHERE tenant_id = ? AND expires_at > NOW() ORDER BY created_at DESC LIMIT 1',
|
|
(string) $tenantId
|
|
);
|
|
|
|
// MintyPHP wraps selectOne results in ['table_name' => [...]]
|
|
$row = is_array($result) ? ($result['helpdesk_oauth_token_cache'] ?? $result) : null;
|
|
if (!is_array($row) || !isset($row['access_token_encrypted'])) {
|
|
return null;
|
|
}
|
|
|
|
$encrypted = trim((string) $row['access_token_encrypted']);
|
|
if ($encrypted === '') {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return $this->settingsCryptoGateway->decryptString($encrypted);
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete all cached tokens (used when global BC config changes).
|
|
*/
|
|
public function deleteAll(): bool
|
|
{
|
|
return (bool) DB::delete('DELETE FROM helpdesk_oauth_token_cache WHERE 1=1');
|
|
}
|
|
|
|
/**
|
|
* Delete cached tokens for a specific tenant (used when tenant config changes).
|
|
*/
|
|
public function deleteByTenantId(int $tenantId): bool
|
|
{
|
|
if ($tenantId <= 0) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) DB::delete(
|
|
'DELETE FROM helpdesk_oauth_token_cache WHERE tenant_id = ?',
|
|
(string) $tenantId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Store a new token in the cache (encrypted, tenant-scoped).
|
|
*/
|
|
public function storeToken(int $tenantId, string $accessToken, \DateTimeInterface $expiresAt): bool
|
|
{
|
|
if ($tenantId <= 0 || $accessToken === '') {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$encrypted = $this->settingsCryptoGateway->encryptString($accessToken);
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
|
|
// Remove existing tokens for this tenant first
|
|
DB::delete(
|
|
'DELETE FROM helpdesk_oauth_token_cache WHERE tenant_id = ?',
|
|
(string) $tenantId
|
|
);
|
|
|
|
return (bool) DB::insert(
|
|
'INSERT INTO helpdesk_oauth_token_cache (tenant_id, access_token_encrypted, expires_at) VALUES (?, ?, ?)',
|
|
(string) $tenantId,
|
|
$encrypted,
|
|
$expiresAt->format('Y-m-d H:i:s')
|
|
);
|
|
}
|
|
}
|