1
0

refactor: enforce layer discipline — move repositories, fix constructor injection

- Move HelpdeskTenantSettingsRepository and HelpdeskTokenRepository from
  Service/ to Repository/ directory (GR-SEC-003: SQL only in repositories)
- Make AccessControl constructor require IntendedUrlService explicitly
  instead of falling back to `new IntendedUrlService()` (GR-TEST-002:
  no service instantiation outside factories)
- Update all imports and tests accordingly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 19:28:07 +02:00
parent a17c2c7cea
commit 4e6b586b77
10 changed files with 21 additions and 12 deletions

View File

@@ -0,0 +1,104 @@
<?php
namespace MintyPHP\Module\Helpdesk\Repository;
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')
);
}
}