chore: consolidate platform split checks
Some checks failed
Dependency Audit / dependency-audit (push) Has been cancelled
Module Matrix / module-matrix (push) Has been cancelled

This commit is contained in:
2026-07-10 12:51:19 +02:00
parent 150b720f12
commit 635d25c74c
216 changed files with 23336 additions and 4077 deletions

View File

@@ -1,14 +1,21 @@
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from sqlalchemy.orm import Session
from govoplan_access.backend.auth.dependencies import ApiPrincipal
from govoplan_access.auth import ApiPrincipal
from govoplan_core.core.change_sequence import record_change
from govoplan_core.core.events import current_event_trace, normalize_trace_id
from govoplan_core.privacy.retention import sanitize_audit_details_for_policy
from govoplan_audit.backend.db.models import AuditLog
AUDIT_MODULE_ID = "audit"
AUDIT_TENANT_EVENTS_COLLECTION = "audit.admin.tenant_events"
AUDIT_SYSTEM_EVENTS_COLLECTION = "audit.admin.system_events"
SENSITIVE_DETAIL_KEYS = {
"password",
"smtp_password",
@@ -16,9 +23,40 @@ SENSITIVE_DETAIL_KEYS = {
"secret",
"api_key",
"token",
"access_token",
"refresh_token",
"authorization",
"cookie",
"credentials",
"credential",
"private_key",
"claim_token",
"build_token",
}
def _optional_text(value: object | None) -> str | None:
if value is None:
return None
candidate = str(value).strip()
return candidate or None
def _compact_trace(trace: Mapping[str, object] | None) -> dict[str, str]:
if not isinstance(trace, Mapping):
return {}
compact: dict[str, str] = {}
correlation_id = _optional_text(trace.get("correlation_id"))
causation_id = _optional_text(trace.get("causation_id"))
clean_correlation_id = normalize_trace_id(correlation_id)
clean_causation_id = normalize_trace_id(causation_id)
if clean_correlation_id:
compact["correlation_id"] = clean_correlation_id
if clean_causation_id:
compact["causation_id"] = clean_causation_id
return compact
def _sanitize_details(value: Any) -> Any:
if isinstance(value, dict):
sanitized: dict[str, Any] = {}
@@ -33,6 +71,70 @@ def _sanitize_details(value: Any) -> Any:
return value
def audit_operation_context(
*,
module_id: object | None = None,
request_id: object | None = None,
run_id: object | None = None,
outcome: object | None = None,
trace: Mapping[str, object] | None = None,
**details: Any,
) -> dict[str, Any]:
"""Build concise operational audit details for admin and lifecycle actions."""
payload: dict[str, Any] = {}
for key, value in (
("module_id", module_id),
("request_id", request_id),
("run_id", run_id),
("outcome", outcome),
):
text = _optional_text(value)
if text is not None:
payload[key] = text
for key, value in details.items():
if value is not None:
payload[key] = _sanitize_details(value)
compact_trace = _compact_trace(trace)
if compact_trace:
existing = payload.get("_trace")
payload["_trace"] = {**existing, **compact_trace} if isinstance(existing, dict) else compact_trace
return payload
def _trace_details(
details: dict[str, Any],
*,
correlation_id: str | None = None,
causation_id: str | None = None,
) -> tuple[dict[str, Any], dict[str, str]]:
active_trace = current_event_trace()
trace: dict[str, str] = {}
clean_correlation_id = normalize_trace_id(correlation_id)
clean_causation_id = normalize_trace_id(causation_id)
if clean_correlation_id or active_trace:
trace["correlation_id"] = clean_correlation_id or active_trace.correlation_id
if clean_causation_id or (active_trace and active_trace.causation_id):
trace["causation_id"] = clean_causation_id or str(active_trace.causation_id)
if not trace:
return details, {}
merged = dict(details)
existing = merged.get("_trace")
merged["_trace"] = {**existing, **trace} if isinstance(existing, dict) else trace
return merged, trace
def _restore_trace_after_policy(details: dict[str, Any], trace: dict[str, str]) -> dict[str, Any]:
if not trace:
return details
if details.get("_trace") == trace:
return details
restored = dict(details)
existing = restored.get("_trace")
restored["_trace"] = {**existing, **trace} if isinstance(existing, dict) else trace
return restored
def audit_event(
session: Session,
*,
@@ -44,6 +146,8 @@ def audit_event(
object_type: str | None = None,
object_id: str | None = None,
details: dict[str, Any] | None = None,
correlation_id: str | None = None,
causation_id: str | None = None,
commit: bool = False,
) -> AuditLog:
"""Persist one audit event.
@@ -56,6 +160,16 @@ def audit_event(
if scope not in {"tenant", "system"}:
raise ValueError(f"Unsupported audit scope: {scope}")
traced_details, trace = _trace_details(
_sanitize_details(details or {}),
correlation_id=correlation_id,
causation_id=causation_id,
)
stored_details = _restore_trace_after_policy(
sanitize_audit_details_for_policy(session, traced_details),
trace,
)
item = AuditLog(
scope=scope,
tenant_id=tenant_id,
@@ -64,13 +178,24 @@ def audit_event(
action=action,
object_type=object_type,
object_id=object_id,
details=sanitize_audit_details_for_policy(session, _sanitize_details(details or {})),
details=stored_details,
)
session.add(item)
session.flush()
record_change(
session,
module_id=AUDIT_MODULE_ID,
collection=AUDIT_SYSTEM_EVENTS_COLLECTION if scope == "system" else AUDIT_TENANT_EVENTS_COLLECTION,
resource_type="audit_log",
resource_id=item.id,
operation="created",
tenant_id=None if scope == "system" else tenant_id,
actor_type="user" if user_id else ("api_key" if api_key_id else None),
actor_id=user_id or api_key_id,
payload={"scope": scope, "action": action, "object_type": object_type, "object_id": object_id},
)
if commit:
session.commit()
else:
session.flush()
return item
@@ -83,6 +208,8 @@ def audit_from_principal(
object_type: str | None = None,
object_id: str | None = None,
details: dict[str, Any] | None = None,
correlation_id: str | None = None,
causation_id: str | None = None,
commit: bool = False,
) -> AuditLog:
return audit_event(
@@ -95,5 +222,7 @@ def audit_from_principal(
object_type=object_type,
object_id=object_id,
details=details,
correlation_id=correlation_id,
causation_id=causation_id,
commit=commit,
)