Zum Inhalt

Audit-Konventionen

Read-only Reference. Was die Operatorin im Audit-Log erwarten kann und was nicht — speziell für Bulk-Aktionen, Anonymous-Actor-Pfade und die JSONB-Search-Semantik.

1 Eine Audit-Zeile pro Bulk-Aktion (TODO-Block-7-4-03)

Operatoren-Bulk-Aktionen (Soft-Delete N Tenants, Bulk-Assign M Module) schreiben eine Audit-Zeile mit entity_id="bulk:<count>" und der vollständigen ID-Liste in details.after. Das ist eine bewusste Compliance-Design-Entscheidung (Block 7.4-Review): ein Reviewer sieht „Operator hat 17 Tenants in einer Aktion soft-gelöscht" als ein Event statt als 17 entkoppelte Events.

Konsequenz für die Suche: Wer im Audit-Log nach einer spezifischen Tenant-/Modul-ID sucht (?entity_id=<uuid>), findet das Bulk-Event nicht — die UUID liegt in details.after, nicht in entity_id. Zwei Wege, den Bulk-Pfad trotzdem zu finden:

  1. Action-Filter: ?action=tenant.bulk_soft_deleted bzw. ?action=tenant_module.bulk_assigned — listet alle Bulk-Events in der Range.
  2. JSON-Search: ?json_search=<uuid-fragment> matcht case-insensitiv auf details::text. Bei < 10 000 Audit-Rows ausreichend (Sequential-Scan < 200 ms). GIN-Index auf details JSONB ist als Folge-TODO geplant, sobald die Tabelle wächst.

Die Aktions-Codes:

Bulk-Action Geschrieben von
tenant.bulk_soft_deleted POST /api/v1/platform/tenants/bulk-soft-delete
tenant_module.bulk_assigned POST /api/v1/tenants/{id}/modules/bulk

2 Anonymous-Actor-Pfad (Block 11)

Public-Widget-Endpoints schreiben Audit-Rows ohne TenantContext. actor_role='anonymous', actor_user='widget', actor_keycloak_id ist NULL. Die tenant_id-Spalte ist gesetzt (aus dem Origin- validierten Chatbot-Lookup). Die Aktions-Codes:

Anonymous-Action Endpoint
chatbot_feedback.created POST /api/v1/feedback

actor_keycloak_id IS NULL ist also kein Bug — es ist die Anonymous-Signatur. Forensisch nutzt die Korrelation tenant_id + details.session_id (vom Widget mitgesendet, falls vorhanden).

3 CSV-Export-Felder (TODO-Block-7-4-02 erledigt)

Seit v1.3.0-D2 enthält der CSV-Export (GET /api/v1/platform/audit/ export.csv) auch die forensischen Felder actor_keycloak_id, ip_address, session_id. Die Header-Reihenfolge ist:

id, occurred_at, actor_role, actor_user, actor_keycloak_id,
tenant_id, chatbot_id, action, entity_type, entity_id,
ip_address, session_id [, details]

Mit ?include_details=true wird die JSONB-Spalte als String-Repr ans Ende angehängt — relevant für die Bulk-Aktions-Lesbarkeit, weil dort die ID-Liste liegt (siehe §1).

4 Date-Range-Validierung (TODO-Block-7-4-07 erledigt)

Beide Audit-Endpoints (GET /api/v1/platform/audit, GET .../export.csv) lehnen ?date_from > date_to mit 422 date_from_after_date_to ab statt einer leeren Liste. Vorher war „kein Audit gefunden" das einzige Signal.

5 Was NICHT auditiert wird

  • Read-Only-Endpoints (alle GET) schreiben keine Audit-Rows.
  • Vendor-Logins landen in vendor_access_log, nicht in platform_audit_log — siehe Block-3-Konzept.
  • Health-Probes und Statics werden nicht geloggt.

6 Retention

platform_audit_log hat eine 2-Jahres-Retention via pg_cron- Archival (Block 1 §6.4). Audit-Rows älter als 730 Tage werden in eine Archiv-Tabelle umgehängt; der Operator-UI-Reader liest nur die Live-Tabelle.