Files
vulncheck/alembic/versions/065_add_audit_log_username.py
T
vulncheck 69d9447de7 feat(audit): every user action names who, what changed and from where
A review for revisionssicher logging asked that the trail answer who, when,
what exactly and from which client IP. Edit asset logged only "Asset
updated", asset and finding actions carried no IP (only logins did), a
deleted user's rows turned into "System/Auto", a manual decommission was
logged as a system action, and a settings change left no row at all.

Every audit row with a user_id now gets the actor's username, client IP and
user agent stamped on insert (before_insert listener, fed by an app-wide
dependency that runs after ProxyHeadersMiddleware), so no writer can forget
them. The username is a snapshot column (migration 065, backfilled), so it
survives delete_user. Rows without a user (sync, scheduler) stay as before.

Asset edits, single and bulk, record each changed field old -> new; a manual
status change carries the user. A settings PUT writes CONFIG_CHANGE with the
changed field names and old/new values whose secret subfields are masked for
every key (ai_config is unprotected and holds api_token). An unchanged
re-save writes nothing. The syslog message carries user=.
2026-10-26 10:24:23 +01:00

32 lines
943 B
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Add audit_logs.username
Revision ID: 065
Revises: 064
Create Date: 2026-10-26 10:00:00.000000
delete_user nulls audit_logs.user_id so the trail survives, but that turned
every row the user wrote into "System/Auto". The actor's name is now stamped
on insert (audit_log._stamp_actor); the backfill names every row whose user
still exists. Rows of users deleted before this revision stay unnamed.
Idempotent (IF NOT EXISTS), same shape as 053–064.
"""
from alembic import op
revision = "065"
down_revision = "064"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute("ALTER TABLE audit_logs ADD COLUMN IF NOT EXISTS username VARCHAR(50);")
op.execute(
"UPDATE audit_logs SET username = users.username FROM users "
"WHERE audit_logs.user_id = users.id AND audit_logs.username IS NULL;"
)
def downgrade() -> None:
op.execute("ALTER TABLE audit_logs DROP COLUMN IF EXISTS username;")