Audit-log bug report status changes (B-60)

Every other admin mutation — config edit, pause/resume, privkey export, password
reset — leaves a trace; this one could silently mark a report resolved. With one
shared ADMIN_TOKEN and no per-admin identity, the audit log is the only
accountability there is.

bug_report_status_changed records the report id and the before/after status, and
carries the report's author as user_id so the entry is traceable from either
side. Nothing is written when the status doesn't actually change, matching
config_updated: an edit that changes nothing isn't an event, and noise hides the
real changes.

Also documents the new event in docs/guida-admin.md's audit table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 22:59:00 +02:00
co-authored by Claude Opus 5
parent 6246b13247
commit 77e07e87dc
5 changed files with 70 additions and 16 deletions
+14 -1
View File
@@ -408,7 +408,20 @@ async def update_bug_report_status(
if report is None:
raise HTTPException(status.HTTP_404_NOT_FOUND, "bug report not found")
report.status = body.status
# B-60: every other admin mutation (config edit, pause/resume, privkey export,
# password reset) leaves a trace; this one silently marked a report `resolved`.
# With one shared ADMIN_TOKEN and no per-admin identity, the audit log is the
# only accountability there is. Before/after like config_updated, and nothing
# written when the status doesn't actually change — re-clicking the status a
# report already has isn't an event.
if report.status != body.status:
await write_audit_log(
session,
"bug_report_status_changed",
{"report_id": report_id, "from": report.status, "to": body.status},
user_id=report.user_id,
)
report.status = body.status
await session.commit()
username = None