• feat : audit log (table + writer + listener + API + admin UI + timeline) (#9)
    Some checks failed
    Auto Tag Develop / tag (push) Successful in 6s
    Build & Push Docker Image / build (push) Failing after 9s

    malio released this 2026-05-13 08:29:30 +00:00

    Résumé

    Implémente le journal d'audit append-only couvrant les 5 tickets de doc/audit-log.md et embarque au passage plusieurs corrections périphériques (sidebar Admin/Mon compte, drawer RBAC, Swagger, schema_filter Doctrine) ainsi que l'initialisation de la suite e2e Playwright. Toutes les mutations Doctrine sur les entités portant #[Auditable] sont tracées dans une table PostgreSQL dédiée, exposée en lecture seule via API Platform et consultable par les admins dans une page dédiée.

    Ce qui change

    Audit log — cœur de la PR

    Backend

    • Migration : table audit_log (UUID v7 natif Postgres en PK, jsonb changes, 3 index pour tri chrono, par entité et par utilisateur).
    • AuditLogWriter : service bas-niveau, écrit via une connexion DBAL dédiée audit (même DSN que default, service séparé) pour sortir de la transaction ORM en batch. Blacklist defense-in-depth password/plainPassword/token/secret.
    • RequestIdProvider : UUID v4 généré au kernel.request principal, injecté dans chaque ligne d'audit de la requête.
    • Attributs #[Auditable] / #[AuditIgnore] dans src/Shared/Domain/Attribute/ (accessibles par tous les modules).
    • AuditListener : capture onFlush / écriture postFlush avec pattern swap-and-clear contre les flushes ré-entrants. Erreurs loguées, jamais propagées. Entité User annotée (password / plainPassword ignorés).
    • API Platform read-only /api/audit-logs (permission RBAC core.audit_log.view) : GET collection paginée + GET item, pas de POST/PUT/PATCH/DELETE. Filtres entity_type, entity_id, action, performed_by, performed_at[after]/[before].
    • DbalPaginator implémentant PaginatorInterface : hydra:view généré automatiquement par API Platform, pas de construction manuelle.
    • Ressource AuditLogEntityTypesResource + provider dédié pour peupler le filtre par type d'entité côté UI (réponse cachée, pas de requête à chaque ouverture du drawer).
    • Permission core.audit_log.view déclarée dans CoreModule::permissions().
    • audit_log exclu du schema_filter Doctrine : plus de faux diff sur make migration-diff.

    Frontend

    • Page admin /admin/audit-log : tableau paginé, filtres locaux (état dans le composant, non persistés dans l'URL — conforme règle CLAUDE.md « Tableaux : pas de persistance URL »), drawer de détail (diff + timeline complète de l'entité), badges colorés par action.
    • Composable partagé useAuditLog avec resetAuditLog() auto-enregistré sur onAuthSessionCleared (règle CLAUDE.md composables singletons).
    • Composant réutilisable <AuditTimeline :entity-type :entity-id> : garde permission (pas d'appel API sans le droit), lazy loading (10 items + bouton « Voir plus »), dates relatives FR via Intl.RelativeTimeFormat, skeleton loader.
    • Entrée sidebar « Journal d'audit » gated sur core.audit_log.view + clés i18n imbriquées dans fr.json.

    Fixes embarqués

    • Review fixes audit-log (commits 37eafd2, 1505e84, 99c77eb) : précision des timestamps, ESCAPE sur les LIKE, plafond pagination, diverses remarques du 1er tour de review.
    • Sidebar (701a480, e2fbf51) : nouvelle section « Administration » + groupe « Mon compte », gate de section sur permissions, « Tableau de bord » déplacé dans « Mon compte ». Convention admin documentée.
    • Drawer RBAC utilisateurs (617ee31, 5f5afcc) : corrige l'affichage des sites et l'écrasement via merge-patch (garde anti-écrasement + spec GET /users/{id}/rbac documentée).
    • Swagger UI (6db955f) : réactivé en ajoutant symfony/twig-bundle aux deps (régression depuis l'arrivée d'API Platform 4.2).
    • phpunit.dist.xml : <env APP_ENV=dev> forçait la suite à tourner sous framework.test=false (→ test.service_container introuvable) ; JWT_PASSPHRASE ne matchait pas les clés de dev. Corrigés pour débloquer la suite.

    E2E Playwright (nouveau, commit 4603ab2)

    • playwright.config.ts + structure frontend/tests/e2e/ (personas, helpers loginAs, page objects LoginPage + SidebarComponent).
    • Specs : auth/login.spec.ts + permissions/sidebar-visibility.spec.ts (vérifie la visibilité de la sidebar par rôle RBAC).
    • Commande SeedE2ECommand pour préparer un jeu de données déterministe côté backend.
    • make e2e ajouté au Makefile.

    Décisions techniques

    • UUID v7 natif Postgres (16 octets vs 36 en varchar) : index performed_at ~40 % plus petit sur une table append-only à croissance infinie.
    • entity_type format module.Entity (ex: core.User) : évite les collisions si deux modules ont des entités de même nom.
    • performed_by dénormalisé (string, pas FK) : le nom persiste même après suppression de l'utilisateur.
    • Connexion DBAL dédiée audit : évite l'entanglement transactionnel entre audit et ORM en batch.
    • ManyToMany non audité : limitation connue (getEntityChangeSet() ne couvre pas les collections) ; extension future via getScheduledCollectionUpdates() si besoin.
    • Filtres locaux non persistés dans l'URL : choix assumé (cf. CLAUDE.md) pour éviter le couplage table ↔ routeur.

    Test plan

    • make test : 218 tests passent (writer unitaires + listener intégration + API fonctionnels + UserRbacProcessor).
    • npm run lint + npm run test + npm run build (frontend).
    • Migration appliquée sur dev + test, audit_log ignoré par schema_filter.
    • Permissions synchronisées (app:sync-permissions).
    • Swagger /api/docs accessible de nouveau.
    • Playwright : make e2e vert en local (login + sidebar-visibility).
    • Vérifier en local : création/modif/suppression d'un user apparaît dans /admin/audit-log.
    • Vérifier : user sans core.audit_log.view → 403 sur l'endpoint + item absent de la sidebar.
    • Vérifier : expansion d'une ligne affiche la timeline de l'entité avec dates relatives FR.
    • Vérifier : drawer RBAC utilisateur n'écrase plus la liste des sites au PATCH.

    Points d'attention pour le review

    • AuditListener : pattern swap-and-clear sur postFlush — relire la gestion des flushes ré-entrants.
    • DbalPaginator : vérifier que l'absence d'Iterator custom ne casse pas la normalisation API Platform sur collections vides.
    • UserRbacProcessor : logique merge-patch + garde anti-écrasement des sites (régression corrigée dans 617ee31).
    • Playwright : nouvelle dépendance de dev, s'assurer que make e2e ne fait pas partie du pipeline CI par défaut (à brancher explicitement).

    Co-authored-by: Matthieu mtholot19@gmail.com
    Reviewed-on: #9
    Co-authored-by: matthieu matthieu@yuno.malio.fr
    Co-committed-by: matthieu matthieu@yuno.malio.fr

    Downloads