forked from fa/breadcrumb-the-shire
refactor(audit): decouple API audit classes from core via interfaces
Core code (lib/) no longer imports any MintyPHP\Module\Audit\* classes directly. New ApiAuditServiceInterface and ApiSystemAuditReporterInterface follow the existing pattern (interface + null implementation in core, concrete binding from module registrar). DoctorRunner and helpers/app.php now resolve through DI interfaces, ensuring the audit module remains fully optional. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,13 @@ use MintyPHP\App\Container\Registrars\SettingsRegistrar;
|
||||
use MintyPHP\App\Container\Registrars\UserRegistrar;
|
||||
use MintyPHP\App\Module\ModuleEventDispatcher;
|
||||
use MintyPHP\App\Module\ModuleRegistry;
|
||||
use MintyPHP\Service\Audit\ApiAuditServiceInterface;
|
||||
use MintyPHP\Service\Audit\ApiSystemAuditReporterInterface;
|
||||
use MintyPHP\Service\Audit\AuditMetadataEnricherInterface;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\Audit\ImportAuditInterface;
|
||||
use MintyPHP\Service\Audit\NullApiAuditService;
|
||||
use MintyPHP\Service\Audit\NullApiSystemAuditReporter;
|
||||
use MintyPHP\Service\Audit\NullAuditMetadataEnricher;
|
||||
use MintyPHP\Service\Audit\NullAuditRecorder;
|
||||
use MintyPHP\Service\Audit\NullImportAudit;
|
||||
@@ -87,5 +91,11 @@ if (!$container->has(ImportAuditInterface::class)) {
|
||||
if (!$container->has(AuditMetadataEnricherInterface::class)) {
|
||||
$container->set(AuditMetadataEnricherInterface::class, static fn (): AuditMetadataEnricherInterface => new NullAuditMetadataEnricher());
|
||||
}
|
||||
if (!$container->has(ApiAuditServiceInterface::class)) {
|
||||
$container->set(ApiAuditServiceInterface::class, static fn (): ApiAuditServiceInterface => new NullApiAuditService());
|
||||
}
|
||||
if (!$container->has(ApiSystemAuditReporterInterface::class)) {
|
||||
$container->set(ApiSystemAuditReporterInterface::class, static fn (): ApiSystemAuditReporterInterface => new NullApiSystemAuditReporter());
|
||||
}
|
||||
|
||||
return $container;
|
||||
|
||||
@@ -8,10 +8,10 @@ use MintyPHP\App\AppContainer;
|
||||
use MintyPHP\App\Bootstrap\EnvValidator;
|
||||
use MintyPHP\Console\Support\CliAppBootstrap;
|
||||
use MintyPHP\DB;
|
||||
use MintyPHP\Module\Audit\Service\SystemAuditService;
|
||||
use MintyPHP\Service\Access\AuthorizationService;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
use MintyPHP\Service\Access\UiAccessService;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\Auth\AuthService;
|
||||
use Throwable;
|
||||
|
||||
@@ -283,7 +283,7 @@ final class DoctorRunner implements DoctorRunnerInterface
|
||||
$durationMs = max(0, (int) round((microtime(true) - $startedAt) * 1000));
|
||||
|
||||
try {
|
||||
app(SystemAuditService::class)->record(
|
||||
app(AuditRecorderInterface::class)->record(
|
||||
'cli.command',
|
||||
$exitCode === 0 ? 'success' : 'failed',
|
||||
[
|
||||
|
||||
18
lib/Service/Audit/ApiAuditServiceInterface.php
Normal file
18
lib/Service/Audit/ApiAuditServiceInterface.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Audit;
|
||||
|
||||
/**
|
||||
* Core-side contract for API request audit tracking.
|
||||
*
|
||||
* Implemented by the audit module's ApiAuditService when the module is active.
|
||||
* Falls back to NullApiAuditService (no-op) when the audit module is disabled.
|
||||
*/
|
||||
interface ApiAuditServiceInterface
|
||||
{
|
||||
public function startRequestContext(): void;
|
||||
|
||||
public function finish(int $statusCode, ?string $errorCode = null): void;
|
||||
|
||||
public function currentRequestId(): ?string;
|
||||
}
|
||||
16
lib/Service/Audit/ApiSystemAuditReporterInterface.php
Normal file
16
lib/Service/Audit/ApiSystemAuditReporterInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Audit;
|
||||
|
||||
/**
|
||||
* Core-side contract for API system-level audit reporting.
|
||||
*
|
||||
* Implemented by the audit module's ApiSystemAuditReporter when the module is active.
|
||||
* Falls back to NullApiSystemAuditReporter (no-op) when the audit module is disabled.
|
||||
*/
|
||||
interface ApiSystemAuditReporterInterface
|
||||
{
|
||||
public function start(): void;
|
||||
|
||||
public function finish(int $statusCode, ?string $errorCode = null): void;
|
||||
}
|
||||
22
lib/Service/Audit/NullApiAuditService.php
Normal file
22
lib/Service/Audit/NullApiAuditService.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Audit;
|
||||
|
||||
/**
|
||||
* No-op API audit service used when the audit module is disabled.
|
||||
*/
|
||||
final class NullApiAuditService implements ApiAuditServiceInterface
|
||||
{
|
||||
public function startRequestContext(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function finish(int $statusCode, ?string $errorCode = null): void
|
||||
{
|
||||
}
|
||||
|
||||
public function currentRequestId(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
17
lib/Service/Audit/NullApiSystemAuditReporter.php
Normal file
17
lib/Service/Audit/NullApiSystemAuditReporter.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Audit;
|
||||
|
||||
/**
|
||||
* No-op API system audit reporter used when the audit module is disabled.
|
||||
*/
|
||||
final class NullApiSystemAuditReporter implements ApiSystemAuditReporterInterface
|
||||
{
|
||||
public function start(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function finish(int $statusCode, ?string $errorCode = null): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -41,24 +41,16 @@ function setAppContainer(\MintyPHP\App\AppContainer $container): void
|
||||
);
|
||||
|
||||
\MintyPHP\Http\ApiBootstrap::configure(
|
||||
$container->has(\MintyPHP\Module\Audit\Service\ApiAuditService::class)
|
||||
? static fn () => $container->get(\MintyPHP\Module\Audit\Service\ApiAuditService::class)
|
||||
: null,
|
||||
static fn (): \MintyPHP\Service\Audit\ApiAuditServiceInterface => $container->get(\MintyPHP\Service\Audit\ApiAuditServiceInterface::class),
|
||||
static fn (): \MintyPHP\Service\Settings\SettingsApiPolicyGateway => $container->get(\MintyPHP\Service\Settings\SettingsApiPolicyGateway::class),
|
||||
static fn (): \MintyPHP\Service\Security\RateLimiterService => $container->get(\MintyPHP\Service\Security\RateLimiterService::class),
|
||||
$container->has(\MintyPHP\Module\Audit\Http\ApiSystemAuditReporter::class)
|
||||
? static fn () => $container->get(\MintyPHP\Module\Audit\Http\ApiSystemAuditReporter::class)
|
||||
: null
|
||||
static fn (): \MintyPHP\Service\Audit\ApiSystemAuditReporterInterface => $container->get(\MintyPHP\Service\Audit\ApiSystemAuditReporterInterface::class)
|
||||
);
|
||||
|
||||
\MintyPHP\Http\ApiResponse::configure(
|
||||
$container->has(\MintyPHP\Module\Audit\Service\ApiAuditService::class)
|
||||
? static fn () => $container->get(\MintyPHP\Module\Audit\Service\ApiAuditService::class)
|
||||
: null,
|
||||
static fn (): \MintyPHP\Service\Audit\ApiAuditServiceInterface => $container->get(\MintyPHP\Service\Audit\ApiAuditServiceInterface::class),
|
||||
static fn (): \MintyPHP\Service\Access\AuthorizationService => $container->get(\MintyPHP\Service\Access\AuthorizationService::class),
|
||||
$container->has(\MintyPHP\Module\Audit\Http\ApiSystemAuditReporter::class)
|
||||
? static fn () => $container->get(\MintyPHP\Module\Audit\Http\ApiSystemAuditReporter::class)
|
||||
: null
|
||||
static fn (): \MintyPHP\Service\Audit\ApiSystemAuditReporterInterface => $container->get(\MintyPHP\Service\Audit\ApiSystemAuditReporterInterface::class)
|
||||
);
|
||||
|
||||
\MintyPHP\Support\Guard::configure(
|
||||
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Module\Audit\Handler\ImportAuditPurgeJobHandler;
|
||||
use MintyPHP\Module\Audit\Handler\SystemAuditPurgeJobHandler;
|
||||
use MintyPHP\Module\Audit\Handler\UserLifecycleAuditPurgeJobHandler;
|
||||
use MintyPHP\Module\Audit\Http\ApiSystemAuditReporter;
|
||||
use MintyPHP\Service\Audit\ApiAuditServiceInterface;
|
||||
use MintyPHP\Service\Audit\ApiSystemAuditReporterInterface;
|
||||
use MintyPHP\Module\Audit\Providers\AuditLayoutProvider;
|
||||
use MintyPHP\Module\Audit\Repository\ApiAuditLogRepository;
|
||||
use MintyPHP\Module\Audit\Repository\ImportAuditRunRepository;
|
||||
@@ -68,6 +70,8 @@ final class AuditContainerRegistrar implements ContainerRegistrar
|
||||
$container->set(AuditRecorderInterface::class, static fn (AppContainer $c): AuditRecorderInterface => $c->get(SystemAuditService::class));
|
||||
$container->set(UserLifecycleAuditInterface::class, static fn (AppContainer $c): UserLifecycleAuditInterface => $c->get(UserLifecycleAuditService::class));
|
||||
$container->set(ImportAuditInterface::class, static fn (AppContainer $c): ImportAuditInterface => $c->get(ImportAuditService::class));
|
||||
$container->set(ApiAuditServiceInterface::class, static fn (AppContainer $c): ApiAuditServiceInterface => $c->get(ApiAuditService::class));
|
||||
$container->set(ApiSystemAuditReporterInterface::class, static fn (AppContainer $c): ApiSystemAuditReporterInterface => $c->get(ApiSystemAuditReporter::class));
|
||||
|
||||
// Frontend telemetry
|
||||
$container->set(FrontendTelemetryIngestService::class, static fn (AppContainer $c): FrontendTelemetryIngestService => new FrontendTelemetryIngestService(
|
||||
|
||||
@@ -6,8 +6,9 @@ use MintyPHP\Http\ApiAuth;
|
||||
use MintyPHP\Http\RequestContext;
|
||||
use MintyPHP\Module\Audit\Domain\SystemAuditOutcome;
|
||||
use MintyPHP\Module\Audit\Service\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\ApiSystemAuditReporterInterface;
|
||||
|
||||
class ApiSystemAuditReporter
|
||||
class ApiSystemAuditReporter implements ApiSystemAuditReporterInterface
|
||||
{
|
||||
/**
|
||||
* @var array{
|
||||
|
||||
@@ -6,8 +6,9 @@ use MintyPHP\Http\ApiAuth;
|
||||
use MintyPHP\Http\RequestContext;
|
||||
use MintyPHP\Http\RequestRuntimeInterface;
|
||||
use MintyPHP\Module\Audit\Repository\ApiAuditLogRepository;
|
||||
use MintyPHP\Service\Audit\ApiAuditServiceInterface;
|
||||
|
||||
class ApiAuditService
|
||||
class ApiAuditService implements ApiAuditServiceInterface
|
||||
{
|
||||
private const RETENTION_DAYS = 90;
|
||||
private const MAX_QUERY_KEYS = 30;
|
||||
|
||||
@@ -17,9 +17,13 @@ final class AuditModuleIsolationContractTest extends TestCase
|
||||
));
|
||||
|
||||
$allowed = [
|
||||
'ApiAuditServiceInterface.php',
|
||||
'ApiSystemAuditReporterInterface.php',
|
||||
'AuditMetadataEnricherInterface.php',
|
||||
'AuditRecorderInterface.php',
|
||||
'ImportAuditInterface.php',
|
||||
'NullApiAuditService.php',
|
||||
'NullApiSystemAuditReporter.php',
|
||||
'NullAuditMetadataEnricher.php',
|
||||
'NullAuditRecorder.php',
|
||||
'NullImportAudit.php',
|
||||
|
||||
Reference in New Issue
Block a user