Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
AuditApiController
100.00% covered (success)
100.00%
50 / 50
100.00% covered (success)
100.00%
4 / 4
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 actionVerifyIntegrity
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
5
 actionListSecurityEvents
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
4
 actionPrune
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Audit\Presentation\Api;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Audit\Application\Service\AuditIntegrityService;
12use App\Core\Audit\Application\Service\AuditRetentionService;
13use App\Core\Audit\Domain\Repository\SecurityAuditRepositoryInterface;
14use App\Shared\Infrastructure\Http\ApiResponseTrait;
15use Psr\Http\Message\ResponseFactoryInterface;
16use Psr\Http\Message\ResponseInterface;
17use Psr\Http\Message\ServerRequestInterface;
18
19/**
20 * Audit Management and Verification REST API Controller.
21 *
22 * Exposes endpoints for cryptographic integrity checks (NIST AU-9), security event inspection,
23 * and automated log retention administration (NIST AU-4).
24 *
25 * @package App\Core\Audit\Presentation\Api
26 */
27final readonly class AuditApiController
28{
29    use ApiResponseTrait;
30
31    /**
32     * AuditApiController constructor.
33     *
34     * @param ResponseFactoryInterface          $factory          PSR response factory.
35     * @param AuditIntegrityService             $integrityService Audit trail cryptographic integrity checker.
36     * @param SecurityAuditRepositoryInterface  $securityRepo     Security incidents repository.
37     * @param AuditRetentionService             $retentionService Audit log retention and pruning service.
38     */
39    public function __construct(
40        private ResponseFactoryInterface         $factory,
41        private AuditIntegrityService            $integrityService,
42        private SecurityAuditRepositoryInterface $securityRepo,
43        private AuditRetentionService            $retentionService,
44    ) {
45    }
46
47    /**
48     * Handles GET /api/v1/audit/integrity for cryptographic chain verification.
49     *
50     * @param ServerRequestInterface $request PSR-7 HTTP request.
51     * @return ResponseInterface JSON response report.
52     */
53    public function actionVerifyIntegrity(ServerRequestInterface $request): ResponseInterface
54    {
55        $targetTable = $request->getQueryParams()['table'] ?? null;
56        $tables = is_string($targetTable) && $targetTable !== '' ? [$targetTable] : [
57            'logs_audit_create_records',
58            'logs_audit_update_records',
59            'logs_audit_delete_records',
60        ];
61
62        $reports = [];
63        $overallValid = true;
64
65        foreach ($tables as $table) {
66            $res = $this->integrityService->verifyTableChain($table);
67            $reports[$table] = $res->toArray();
68            if (!$res->isValid) {
69                $overallValid = false;
70            }
71        }
72
73        return $this->jsonSuccess($this->factory, [
74            'overall_valid' => $overallValid,
75            'tables'        => $reports,
76            'verified_at'   => date('Y-m-d H:i:s.u'),
77        ]);
78    }
79
80    /**
81     * Handles GET /api/v1/audit/security-events for incident log inspection.
82     *
83     * @param ServerRequestInterface $request PSR-7 HTTP request.
84     * @return ResponseInterface JSON list of security events.
85     */
86    public function actionListSecurityEvents(ServerRequestInterface $request): ResponseInterface
87    {
88        $params = $request->getQueryParams();
89        $limit = max(1, min(100, (int) ($params['limit'] ?? 25)));
90        $page = max(1, (int) ($params['page'] ?? 1));
91        $offset = ($page - 1) * $limit;
92
93        $criteria = [];
94        if (!empty($params['event_type'])) {
95            $criteria['event_type'] = (string) $params['event_type'];
96        }
97        if (!empty($params['severity'])) {
98            $criteria['severity'] = (string) $params['severity'];
99        }
100        if (!empty($params['request_id'])) {
101            $criteria['request_id'] = (string) $params['request_id'];
102        }
103
104        $events = $this->securityRepo->find($criteria, $limit, $offset);
105        $total = $this->securityRepo->count($criteria);
106
107        $data = array_map(static fn($e): array => $e->toArray(), $events);
108
109        return $this->jsonSuccess($this->factory, [
110            'records'     => $data,
111            'total'       => $total,
112            'page'        => $page,
113            'limit'       => $limit,
114            'total_pages' => (int) ceil($total / $limit),
115        ]);
116    }
117
118    /**
119     * Handles POST /api/v1/audit/prune for manual/scheduled log pruning.
120     *
121     * @param ServerRequestInterface $request PSR-7 HTTP request.
122     * @return ResponseInterface JSON pruning summary report.
123     */
124    public function actionPrune(ServerRequestInterface $request): ResponseInterface
125    {
126        $body = (array) $request->getParsedBody();
127        $auditDays = max(1, (int) ($body['audit_retention_days'] ?? 180));
128        $securityDays = max(1, (int) ($body['security_retention_days'] ?? 365));
129
130        $pruned = $this->retentionService->pruneExpiredLogs($auditDays, $securityDays);
131
132        return $this->jsonSuccess($this->factory, [
133            'pruned'     => $pruned,
134            'total'      => array_sum($pruned),
135            'audit_days' => $auditDays,
136            'sec_days'   => $securityDays,
137        ]);
138    }
139}