92 lines
2.2 KiB
Python
92 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.auth.dependencies import ApiPrincipal
|
|
from app.db.models import AuditLog
|
|
|
|
|
|
SENSITIVE_DETAIL_KEYS = {
|
|
"password",
|
|
"smtp_password",
|
|
"imap_password",
|
|
"secret",
|
|
"api_key",
|
|
"token",
|
|
}
|
|
|
|
|
|
def _sanitize_details(value: Any) -> Any:
|
|
if isinstance(value, dict):
|
|
sanitized: dict[str, Any] = {}
|
|
for key, item in value.items():
|
|
if str(key).lower() in SENSITIVE_DETAIL_KEYS:
|
|
sanitized[key] = "<redacted>"
|
|
else:
|
|
sanitized[key] = _sanitize_details(item)
|
|
return sanitized
|
|
if isinstance(value, list):
|
|
return [_sanitize_details(item) for item in value]
|
|
return value
|
|
|
|
|
|
def audit_event(
|
|
session: Session,
|
|
*,
|
|
tenant_id: str | None,
|
|
action: str,
|
|
user_id: str | None = None,
|
|
api_key_id: str | None = None,
|
|
object_type: str | None = None,
|
|
object_id: str | None = None,
|
|
details: dict[str, Any] | None = None,
|
|
commit: bool = False,
|
|
) -> AuditLog:
|
|
"""Persist one audit event.
|
|
|
|
The function deliberately accepts primitive IDs so it can be used from API
|
|
handlers, CLI commands and worker code without coupling the lower-level
|
|
services to FastAPI request objects.
|
|
"""
|
|
|
|
item = AuditLog(
|
|
tenant_id=tenant_id,
|
|
user_id=user_id,
|
|
api_key_id=api_key_id,
|
|
action=action,
|
|
object_type=object_type,
|
|
object_id=object_id,
|
|
details=_sanitize_details(details or {}),
|
|
)
|
|
session.add(item)
|
|
if commit:
|
|
session.commit()
|
|
else:
|
|
session.flush()
|
|
return item
|
|
|
|
|
|
def audit_from_principal(
|
|
session: Session,
|
|
principal: ApiPrincipal,
|
|
*,
|
|
action: str,
|
|
object_type: str | None = None,
|
|
object_id: str | None = None,
|
|
details: dict[str, Any] | None = None,
|
|
commit: bool = False,
|
|
) -> AuditLog:
|
|
return audit_event(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
user_id=principal.user.id,
|
|
api_key_id=principal.api_key.id if principal.api_key else None,
|
|
action=action,
|
|
object_type=object_type,
|
|
object_id=object_id,
|
|
details=details,
|
|
commit=commit,
|
|
)
|