Modifiche varie

This commit is contained in:
2026-06-04 20:54:49 +02:00
parent ccc4167e28
commit e31676d22e
31 changed files with 3058 additions and 153 deletions
+75 -18
View File
@@ -1,8 +1,9 @@
"""
Router Audit Log consultazione degli eventi di sistema.
Router Audit Log consultazione ed esportazione degli eventi di sistema.
Endpoint:
GET /api/v1/audit-log lista paginata con filtri (solo admin/super_admin)
GET /api/v1/audit-log lista paginata con filtri (solo admin/super_admin)
GET /api/v1/audit-log/export esportazione CSV o PDF (solo admin/super_admin)
Permessi:
- admin: vede solo gli eventi del proprio tenant
@@ -14,6 +15,7 @@ from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Query
from fastapi.responses import Response, StreamingResponse
from app.dependencies import AdminUser, DB
from app.schemas.audit_log import AuditLogListResponse
@@ -22,6 +24,13 @@ from app.services.audit_service import AuditService
router = APIRouter(prefix="/audit-log", tags=["Audit Log"])
def _effective_tenant_id(current_user, tenant_id: Optional[uuid.UUID]) -> Optional[uuid.UUID]:
"""Determina il tenant_id effettivo in base al ruolo dell'utente."""
if current_user.is_super_admin:
return tenant_id # None = tutti i tenant
return current_user.tenant_id # sempre vincolato al proprio tenant
@router.get("", response_model=AuditLogListResponse)
async def list_audit_log(
current_user: AdminUser,
@@ -36,24 +45,10 @@ async def list_audit_log(
resource_type: Optional[str] = Query(None, description="Tipo risorsa (user, mailbox, message, ...)"),
tenant_id: Optional[uuid.UUID] = Query(None, description="Filtra per tenant (solo super_admin)"),
) -> AuditLogListResponse:
"""
Restituisce la lista paginata degli eventi di audit.
- Admin: vede solo gli eventi del proprio tenant (tenant_id ignorato).
- Super Admin: vede tutti i tenant, filtrabile per tenant_id.
"""
"""Restituisce la lista paginata degli eventi di audit."""
svc = AuditService(db)
# Determina il tenant_id effettivo da applicare al filtro
if current_user.is_super_admin:
# Super admin: usa il tenant_id passato come filtro (None = tutti)
effective_tenant_id = tenant_id
else:
# Admin normale: sempre vincolato al proprio tenant
effective_tenant_id = current_user.tenant_id
return await svc.list(
tenant_id=effective_tenant_id,
tenant_id=_effective_tenant_id(current_user, tenant_id),
page=page,
page_size=page_size,
action=action,
@@ -63,3 +58,65 @@ async def list_audit_log(
date_to=date_to,
resource_type=resource_type,
)
@router.get("/export")
async def export_audit_log(
current_user: AdminUser,
db: DB,
format: str = Query("csv", pattern="^(csv|pdf)$", description="Formato: csv o pdf"),
action: Optional[str] = Query(None),
user_id: Optional[uuid.UUID] = Query(None),
outcome: Optional[str] = Query(None, pattern="^(success|failure)$"),
date_from: Optional[datetime] = Query(None),
date_to: Optional[datetime] = Query(None),
resource_type: Optional[str] = Query(None),
tenant_id: Optional[uuid.UUID] = Query(None),
) -> Response:
"""
Esporta i log di audit in formato CSV o PDF.
Applica gli stessi filtri dell'endpoint lista.
"""
svc = AuditService(db)
effective_tid = _effective_tenant_id(current_user, tenant_id)
# Nome file con periodo
suffix = ""
if date_from:
suffix += f"_dal_{date_from.strftime('%Y%m%d')}"
if date_to:
suffix += f"_al_{date_to.strftime('%Y%m%d')}"
if format == "csv":
csv_content = await svc.export_csv(
tenant_id=effective_tid,
action=action,
user_id=user_id,
outcome=outcome,
date_from=date_from,
date_to=date_to,
resource_type=resource_type,
)
filename = f"audit_log{suffix}.csv"
return Response(
content=csv_content.encode("utf-8-sig"), # BOM per compatibilita' Excel
media_type="text/csv; charset=utf-8",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
else: # pdf
pdf_bytes = await svc.export_pdf_bytes(
tenant_id=effective_tid,
action=action,
user_id=user_id,
outcome=outcome,
date_from=date_from,
date_to=date_to,
resource_type=resource_type,
)
filename = f"audit_log{suffix}.pdf"
return Response(
content=pdf_bytes,
media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)