groupe-meditation/backend/app/core/audit.py
2026-05-25 17:39:38 -04:00

78 lines
2.6 KiB
Python

"""Journalisation automatique des changements API."""
from jose import JWTError, jwt
from sqlalchemy import select
from starlette.middleware.base import BaseHTTPMiddleware
from app.core.config import settings
from app.core.database import async_session
from app.models.journal_action import JournalAction
from app.models.membre import Membre
from app.services.notifications import notify_group_change
MUTATING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
SKIPPED_PREFIXES = (
"/api/auth/connexion",
"/api/health",
)
def _token_payload(request):
header = request.headers.get("authorization") or ""
if not header.lower().startswith("bearer "):
return None
token = header.split(" ", 1)[1].strip()
try:
return jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM])
except JWTError:
return None
class AuditMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
path = request.url.path
if (
request.method not in MUTATING_METHODS
or not path.startswith("/api/")
or any(path.startswith(prefix) for prefix in SKIPPED_PREFIXES)
or response.status_code >= 400
):
return response
payload = _token_payload(request)
if not payload or not payload.get("sub"):
return response
async with async_session() as db:
membre = None
result = await db.execute(select(Membre).where(Membre.id == payload["sub"]))
membre = result.scalar_one_or_none()
if not membre:
return response
details = {
"query": dict(request.query_params),
"impersonification": bool(payload.get("adm")),
}
db.add(JournalAction(
groupe_id=membre.groupe_id,
membre_id=membre.id,
admin_id=payload.get("adm"),
methode=request.method,
chemin=path,
statut_http=response.status_code,
adresse_ip=request.client.host if request.client else None,
user_agent=(request.headers.get("user-agent") or "")[:255] or None,
details=details,
))
await notify_group_change(
db,
groupe_id=membre.groupe_id,
actor_id=membre.id,
title="Changement dans l'application",
body=f"{membre.prenom} a enregistré une modification.",
url="/",
)
await db.commit()
return response