73 lines
2.2 KiB
Python
73 lines
2.2 KiB
Python
from __future__ import annotations
|
|
import uuid
|
|
from typing import Any
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from ..db.models import AuditEvent
|
|
from ..core.time import utc_now
|
|
from ..core.redaction import redact_message, redact_value
|
|
|
|
|
|
def _redact_mapping(value: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
return redact_value(value) if value else None
|
|
|
|
|
|
def log_event(
|
|
session: Session,
|
|
*,
|
|
source: str,
|
|
identity: str,
|
|
action_id: str,
|
|
target: str,
|
|
risk: str,
|
|
policy_decision: str,
|
|
reason: str,
|
|
result: str,
|
|
target_type: str = 'app',
|
|
execution_plan: dict[str, Any] | None = None,
|
|
before_state: dict[str, Any] | None = None,
|
|
after_state: dict[str, Any] | None = None,
|
|
post_checks: dict[str, Any] | None = None,
|
|
redacted_metadata: dict[str, Any] | None = None,
|
|
) -> AuditEvent:
|
|
from .user_access import actor_identity
|
|
user_identity = actor_identity.get()
|
|
if user_identity and identity in {"manual", "voice", "automation", "system"}:
|
|
# Keep the request channel separately from the authenticated actor.
|
|
redacted_metadata = {**(redacted_metadata or {}), "requestSource": identity}
|
|
identity = user_identity or identity
|
|
event = AuditEvent(
|
|
id=f'audit-{uuid.uuid4().hex[:12]}',
|
|
timestamp=utc_now(),
|
|
source=source,
|
|
identity=identity,
|
|
action_id=redact_message(action_id),
|
|
target=redact_message(target),
|
|
target_type=redact_message(target_type),
|
|
risk=risk,
|
|
policy_decision=policy_decision,
|
|
reason=redact_message(reason),
|
|
execution_plan=_redact_mapping(execution_plan) or {},
|
|
result=result,
|
|
before_state=_redact_mapping(before_state),
|
|
after_state=_redact_mapping(after_state),
|
|
post_checks=_redact_mapping(post_checks),
|
|
redacted_metadata=_redact_mapping(redacted_metadata),
|
|
)
|
|
|
|
session.add(event)
|
|
session.commit()
|
|
session.refresh(event)
|
|
return event
|
|
|
|
|
|
def list_events(session: Session, *, limit: int = 200, offset: int = 0):
|
|
statement = (
|
|
select(AuditEvent)
|
|
.order_by(AuditEvent.timestamp.desc(), AuditEvent.id.desc())
|
|
.offset(max(0, offset))
|
|
.limit(max(1, min(limit, 500)))
|
|
)
|
|
return session.exec(statement).all()
|