64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""Audit middleware: log all mutating requests to AuditLog."""
|
|
import json
|
|
|
|
from fastapi import Request
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
from starlette.responses import Response
|
|
|
|
from app.database import async_session
|
|
from app.models.audit import AuditLog
|
|
from app.services.auth_service import decode_access_token, get_user_by_id
|
|
|
|
|
|
class AuditMiddleware(BaseHTTPMiddleware):
|
|
async def dispatch(self, request: Request, call_next) -> Response:
|
|
if request.method in ("POST", "PUT", "PATCH", "DELETE"):
|
|
response = await call_next(request)
|
|
await self._log(request, response)
|
|
return response
|
|
return await call_next(request)
|
|
|
|
async def _log(self, request: Request, response: Response):
|
|
try:
|
|
user_id = "anonymous"
|
|
username = "anonymous"
|
|
|
|
auth_header = request.headers.get("authorization")
|
|
if auth_header and auth_header.startswith("Bearer "):
|
|
try:
|
|
payload = decode_access_token(auth_header[7:])
|
|
async with async_session() as db:
|
|
user = await get_user_by_id(db, payload["sub"])
|
|
if user:
|
|
user_id = user.id
|
|
username = user.username
|
|
except Exception:
|
|
pass
|
|
|
|
path = request.url.path
|
|
action = f"{request.method} {path}"
|
|
|
|
detail = {}
|
|
if response.status_code >= 400:
|
|
body = b""
|
|
try:
|
|
body = await request.body()
|
|
if body:
|
|
detail["request_body"] = json.loads(body)
|
|
except Exception:
|
|
pass
|
|
|
|
async with async_session() as db:
|
|
log = AuditLog(
|
|
user_id=user_id,
|
|
username=username,
|
|
action=action,
|
|
target=path,
|
|
detail=json.dumps(detail) if detail else None,
|
|
ip_address=request.client.host if request.client else None,
|
|
)
|
|
db.add(log)
|
|
await db.commit()
|
|
except Exception:
|
|
pass
|