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
103 lines
3.2 KiB
PHP
103 lines
3.2 KiB
PHP
<?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);
|
|
}
|
|
}
|