infrasynth-backend-kit/infrasynth/audit/middleware.py
2026-08-28 14:38:47 -05:00

80 lines
2.6 KiB
Python

import time
import uuid
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from .models import APIInteractionLog
class AuditAPIMiddleware(MiddlewareMixin):
def process_request(self, request):
request.request_id = str(uuid.uuid4())
request._audit_start_time = time.time()
def process_response(self, request, response):
config = getattr(settings, "INFRASYNTH_AUDIT", {})
if not config.get("ENABLE_API_LOGGING", True):
return response
path = request.path
if path.startswith("/admin/"):
return response
if hasattr(request, "_audit_start_time"):
duration_ms = int((time.time() - request._audit_start_time) * 1000)
else:
duration_ms = 0
max_body = config.get("MAX_BODY_SIZE_BYTES", 5000)
request_body = None
response_body = None
sensitive_keys = config.get("SENSITIVE_KEYS", [])
try:
raw_body = getattr(request, "body", b"")
if raw_body and len(raw_body) <= max_body:
body = raw_body.decode("utf-8", errors="replace")
if not any(k in body.lower() for k in sensitive_keys):
import json
try:
request_body = json.loads(body)
except (json.JSONDecodeError, ValueError):
request_body = {"_truncated": True}
except Exception:
pass
try:
if hasattr(response, "data") and response.data:
import json as _json
try:
raw = _json.dumps(response.data)
if len(raw) <= max_body:
response_body = response.data
except (TypeError, ValueError):
pass
except Exception:
pass
actor = getattr(request, "user", None)
if actor and not actor.is_authenticated:
actor = None
if actor is not None and not hasattr(actor, "_meta"):
actor = None
APIInteractionLog.objects.create(
method=request.method,
path=path,
status_code=response.status_code,
request_body=request_body,
response_body=response_body,
ip_address=request.META.get("REMOTE_ADDR"),
actor=actor,
duration_ms=duration_ms,
request_id=getattr(request, "request_id", ""),
user_agent=request.META.get("HTTP_USER_AGENT", ""),
)
return response