feat(audit) : expose le contexte forensique dans l'API lecture
This commit is contained in:
@@ -8,5 +8,9 @@ export type AuditLog = {
|
|||||||
description: string
|
description: string
|
||||||
changes: { old?: Record<string, unknown>; new?: Record<string, unknown> } | null
|
changes: { old?: Record<string, unknown>; new?: Record<string, unknown> } | null
|
||||||
affectedDate: string | null
|
affectedDate: string | null
|
||||||
|
ipAddress: string | null
|
||||||
|
userAgent: string | null
|
||||||
|
deviceLabel: string | null
|
||||||
|
deviceId: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
use App\Entity\AuditLog;
|
use App\Entity\AuditLog;
|
||||||
|
use App\Repository\Contract\AuditLogReadRepositoryInterface;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
@@ -12,7 +13,7 @@ use Doctrine\Persistence\ManagerRegistry;
|
|||||||
/**
|
/**
|
||||||
* @extends ServiceEntityRepository<AuditLog>
|
* @extends ServiceEntityRepository<AuditLog>
|
||||||
*/
|
*/
|
||||||
final class AuditLogRepository extends ServiceEntityRepository
|
final class AuditLogRepository extends ServiceEntityRepository implements AuditLogReadRepositoryInterface
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repository\Contract;
|
||||||
|
|
||||||
|
use App\Entity\AuditLog;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
interface AuditLogReadRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return list<AuditLog>
|
||||||
|
*/
|
||||||
|
public function findByFilters(
|
||||||
|
?int $employeeId = null,
|
||||||
|
?DateTimeImmutable $from = null,
|
||||||
|
?DateTimeImmutable $to = null,
|
||||||
|
?string $entityType = null,
|
||||||
|
int $limit = 50,
|
||||||
|
int $offset = 0,
|
||||||
|
): array;
|
||||||
|
|
||||||
|
public function countByFilters(
|
||||||
|
?int $employeeId = null,
|
||||||
|
?DateTimeImmutable $from = null,
|
||||||
|
?DateTimeImmutable $to = null,
|
||||||
|
?string $entityType = null,
|
||||||
|
): int;
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace App\State;
|
|||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
use App\Repository\AuditLogRepository;
|
use App\Repository\Contract\AuditLogReadRepositoryInterface;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
@@ -18,7 +18,7 @@ class AuditLogProvider implements ProviderInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly RequestStack $requestStack,
|
private readonly RequestStack $requestStack,
|
||||||
private readonly AuditLogRepository $auditLogRepository,
|
private readonly AuditLogReadRepositoryInterface $auditLogRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): JsonResponse
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): JsonResponse
|
||||||
@@ -60,6 +60,10 @@ class AuditLogProvider implements ProviderInterface
|
|||||||
'description' => $log->getDescription(),
|
'description' => $log->getDescription(),
|
||||||
'changes' => $log->getChanges(),
|
'changes' => $log->getChanges(),
|
||||||
'affectedDate' => $log->getAffectedDate()?->format('Y-m-d'),
|
'affectedDate' => $log->getAffectedDate()?->format('Y-m-d'),
|
||||||
|
'ipAddress' => $log->getIpAddress(),
|
||||||
|
'userAgent' => $log->getUserAgent(),
|
||||||
|
'deviceLabel' => $log->getDeviceLabel(),
|
||||||
|
'deviceId' => $log->getDeviceId(),
|
||||||
'createdAt' => $log->getCreatedAt()->setTimezone(new DateTimeZone('Europe/Paris'))->format('Y-m-d H:i:s'),
|
'createdAt' => $log->getCreatedAt()->setTimezone(new DateTimeZone('Europe/Paris'))->format('Y-m-d H:i:s'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\State;
|
||||||
|
|
||||||
|
use ApiPlatform\Metadata\Operation;
|
||||||
|
use App\Entity\AuditLog;
|
||||||
|
use App\Repository\Contract\AuditLogReadRepositoryInterface;
|
||||||
|
use App\State\AuditLogProvider;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class AuditLogProviderTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testProvideExposesForensicFields(): void
|
||||||
|
{
|
||||||
|
$log = new AuditLog()
|
||||||
|
->setUsername('usine')
|
||||||
|
->setAction('create')
|
||||||
|
->setEntityType('work_hour')
|
||||||
|
->setDescription('desc')
|
||||||
|
->setIpAddress('203.0.113.7')
|
||||||
|
->setUserAgent('UA-string')
|
||||||
|
->setDeviceLabel('Mobile · Android · Chrome')
|
||||||
|
->setDeviceId('device-abc')
|
||||||
|
;
|
||||||
|
|
||||||
|
$repo = $this->createMock(AuditLogReadRepositoryInterface::class);
|
||||||
|
$repo->method('countByFilters')->willReturn(1);
|
||||||
|
$repo->method('findByFilters')->willReturn([$log]);
|
||||||
|
|
||||||
|
$stack = new RequestStack();
|
||||||
|
$stack->push(Request::create('/api/audit-logs', 'GET'));
|
||||||
|
|
||||||
|
$provider = new AuditLogProvider($stack, $repo);
|
||||||
|
$response = $provider->provide($this->createMock(Operation::class));
|
||||||
|
|
||||||
|
$data = json_decode((string) $response->getContent(), true);
|
||||||
|
$item = $data['items'][0];
|
||||||
|
|
||||||
|
self::assertSame('203.0.113.7', $item['ipAddress']);
|
||||||
|
self::assertSame('UA-string', $item['userAgent']);
|
||||||
|
self::assertSame('Mobile · Android · Chrome', $item['deviceLabel']);
|
||||||
|
self::assertSame('device-abc', $item['deviceId']);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user