Add compliance audit log

Immutable audit_log table in the scanner DB records every significant
admin action (profile save/delete, token create/revoke, PIN changes,
source add/update/delete, scheduler job changes, scan start/stop, SMTP
save, dispositions, item delete/redact). GET /api/audit_log exposes
entries newest-first. New Audit Log tab in the Settings modal renders
the table on demand. Settings modal widened 540→640 px and tab labels
set to white-space:nowrap so the six-tab row fits on one line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
StyxX65andClaude Sonnet 4.6 committed 2026-05-28 10:51:23 +02:00
1 parent 4ef2dfb352
commit 744813f4ac
18 files changed
+236 -11

No files matched your search

+12 -2
View File
@@ -11,11 +11,12 @@ from checkpoint import _clear_checkpoint, _DELTA_PATH
from cpr_detector import _extract_exif, _html_esc, _placeholder_svg
try:
from gdpr_db import get_db as _get_db
from gdpr_db import get_db as _get_db, log_audit_event as _audit
DB_OK = True
except ImportError:
DB_OK = False
def _get_db(*a, **kw): return None # type: ignore[misc]
def _audit(*a, **kw): pass # type: ignore[misc]
try:
import document_scanner as _ds # noqa: F401
@@ -140,6 +141,9 @@ def db_set_disposition():
notes = data.get("notes", ""),
reviewed_by = data.get("reviewed_by", ""),
)
_audit("disposition",
f"item_id={item_id!r} status={data.get('status','')!r}",
ip=request.remote_addr or "")
return jsonify({"status": "saved"})
@@ -160,6 +164,9 @@ def db_set_disposition_bulk():
legal_basis=data.get("legal_basis", ""),
notes=data.get("notes", ""),
reviewed_by=data.get("reviewed_by", ""))
_audit("disposition_bulk",
f"count={len(item_ids)} status={status!r}",
ip=request.remote_addr or "")
return jsonify({"saved": len(item_ids)})
@@ -281,10 +288,13 @@ def admin_pin_set():
new_pin = data.get("new_pin", "").strip()
if not new_pin:
return jsonify({"error": "new_pin required"}), 400
if _admin_pin_is_set():
had_pin = _admin_pin_is_set()
if had_pin:
if not _verify_admin_pin(data.get("current_pin", "")):
return jsonify({"error": "incorrect_pin"}), 403
_set_admin_pin(new_pin)
_audit("admin_pin_change" if had_pin else "admin_pin_set", "",
ip=request.remote_addr or "")
return jsonify({"ok": True})