feat: Audit trail pour actions sensibles
Story 1.7 - Implémente un système complet d'audit trail pour tracer toutes les actions sensibles (authentification, modifications de données, exports) avec immuabilité garantie par PostgreSQL. Fonctionnalités principales: - Table audit_log append-only avec contraintes PostgreSQL (RULE) - AuditLogger centralisé avec injection automatique du contexte - Correlation ID pour traçabilité distribuée (HTTP + async) - Handlers pour événements d'authentification - Commande d'archivage des logs anciens - Pas de PII dans les logs (emails/IPs hashés) Infrastructure: - Middlewares Messenger pour propagation du Correlation ID - HTTP middleware pour génération/propagation du Correlation ID - Support multi-tenant avec TenantResolver
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Functional\Administration\Api;
|
||||
|
||||
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
||||
/**
|
||||
* [P0] Functional tests for account activation endpoints.
|
||||
*
|
||||
* Verifies:
|
||||
* - Token info endpoint accessibility (public)
|
||||
* - Activate endpoint accessibility (public)
|
||||
* - Token validation
|
||||
* - Password requirements validation
|
||||
*/
|
||||
final class ActivationEndpointsTest extends ApiTestCase
|
||||
{
|
||||
protected static ?bool $alwaysBootKernel = true;
|
||||
|
||||
#[Test]
|
||||
public function activationTokenInfoEndpointIsAccessibleWithoutAuthentication(): void
|
||||
{
|
||||
// GIVEN: No authentication
|
||||
$client = static::createClient();
|
||||
|
||||
// WHEN: Requesting token info for an invalid token
|
||||
$response = $client->request('GET', '/api/activation-tokens/550e8400-e29b-41d4-a716-446655440000', [
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
],
|
||||
]);
|
||||
|
||||
// THEN: Returns 404 (not 401) because endpoint is public
|
||||
// Invalid token returns 404 Not Found
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activateEndpointIsAccessibleWithoutAuthentication(): void
|
||||
{
|
||||
// GIVEN: No authentication
|
||||
$client = static::createClient();
|
||||
|
||||
// WHEN: Attempting to activate with invalid token
|
||||
$response = $client->request('POST', '/api/activate', [
|
||||
'json' => [
|
||||
'tokenValue' => '550e8400-e29b-41d4-a716-446655440000',
|
||||
'password' => 'ValidPassword123!',
|
||||
],
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
],
|
||||
]);
|
||||
|
||||
// THEN: Returns 404 (token not found) not 401 (unauthorized)
|
||||
// The endpoint is accessible without JWT, it validates the token's existence
|
||||
self::assertNotEquals(401, $response->getStatusCode(), 'Activation endpoint should be accessible without JWT');
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activateEndpointValidatesPasswordRequirements(): void
|
||||
{
|
||||
// GIVEN: No authentication
|
||||
$client = static::createClient();
|
||||
|
||||
// WHEN: Attempting activation with weak password
|
||||
$response = $client->request('POST', '/api/activate', [
|
||||
'json' => [
|
||||
'tokenValue' => '550e8400-e29b-41d4-a716-446655440000',
|
||||
'password' => 'weak', // Too short, no uppercase, no number, no special char
|
||||
],
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
],
|
||||
]);
|
||||
|
||||
// THEN: Returns 422 Unprocessable Entity for validation errors
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function activateEndpointRequiresTokenAndPassword(): void
|
||||
{
|
||||
// GIVEN: No authentication
|
||||
$client = static::createClient();
|
||||
|
||||
// WHEN: Attempting activation without required fields
|
||||
$response = $client->request('POST', '/api/activate', [
|
||||
'json' => [],
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
],
|
||||
]);
|
||||
|
||||
// THEN: Returns 422 for missing required fields
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user