feat: systeme de logs d'activite admin avec HMAC + export PDF

src/Entity/AppLog.php (nouveau):
- id, user (ManyToOne nullable, SET NULL on delete), method (GET/POST/etc),
  url (500 chars), route (nom de la route Symfony), action (description
  lisible de l'action), ip (nullable), hmac (SHA-256), createdAt
- Index sur created_at pour les requetes paginées
- HMAC genere dans le constructeur avec payload:
  method|url|route|action|ip|userId|createdAt (microsecondes)
- verifyHmac(): verification timing-safe avec hash_equals
- Aucun setter sur les champs (immutable apres creation)

src/Repository/AppLogRepository.php (nouveau):
- createPaginatedQueryBuilder(): ORDER BY createdAt DESC avec jointure user

src/Service/AppLoggerService.php (nouveau):
- Dictionnaire ROUTE_LABELS: 30+ routes admin avec descriptions
  lisibles (ex: app_admin_clients_create → "Creation d'un client")
- log(): cree un AppLog avec l'action lisible, persiste et flush
- verifyLog(): verifie le HMAC d'un log
- Si la route n'est pas dans le dictionnaire, utilise "Acces a {route}"
- Ajoute "(soumission)" pour les POST

src/EventListener/AdminLogListener.php (nouveau):
- Ecoute KernelEvents::CONTROLLER avec priorite -10
- Intercepte toutes les requetes dont la route commence par app_admin_
- Ignore les requetes AJAX de recherche (evite le spam)
- Recupere l'utilisateur connecte via TokenStorage
- Appelle AppLoggerService::log() dans un try/catch
  (ne bloque jamais la requete si le logging echoue)

src/Controller/Admin/LogsController.php (nouveau):
- Route /admin/logs, ROLE_ROOT
- index(): pagination KnpPaginator (20 par page), verifie le HMAC
  de chaque log affiche
- pdf(): genere un PDF Dompdf avec toutes les infos du log
  + verification HMAC (CONFORME vert / ALTEREES rouge)

templates/admin/logs/index.html.twig (nouveau):
- Tableau glassmorphism: date, utilisateur, methode (badge colore),
  action, URL (tronquee), IP, colonne HMAC (rond vert/rouge),
  bouton PDF par ligne
- Pagination KnpPaginator en bas

templates/admin/logs/pdf.html.twig (nouveau):
- PDF A4 avec tableau d'informations du log
- Bloc HMAC avec fond vert "INTEGRITE VERIFIEE" ou rouge
  "INTEGRITE COMPROMISE" + signature HMAC complete
- Footer avec mention SARL SITECONSEIL

templates/admin/_layout.html.twig:
- Ajout lien "Logs" dans la sidebar Super Admin avec icone document

migrations/Version20260402211054.php:
- Table app_log avec FK user_id, index sur created_at

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-02 23:11:34 +02:00
parent ecc9ec82b7
commit 9c1ea29505
9 changed files with 530 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260402211054 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE app_log (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, method VARCHAR(10) NOT NULL, url VARCHAR(500) NOT NULL, route VARCHAR(255) NOT NULL, action TEXT NOT NULL, ip VARCHAR(50) DEFAULT NULL, hmac VARCHAR(128) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_A14DDB02A76ED395 ON app_log (user_id)');
$this->addSql('CREATE INDEX idx_applog_created ON app_log (created_at)');
$this->addSql('ALTER TABLE app_log ADD CONSTRAINT FK_A14DDB02A76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE app_log DROP CONSTRAINT FK_A14DDB02A76ED395');
$this->addSql('DROP TABLE app_log');
}
}