feat(10-01): add audit event logging to config backup operations

- config_snapshot_created event after successful snapshot INSERT
- config_snapshot_skipped_duplicate event on dedup match
- config_diff_generated event after diff INSERT
- config_backup_manual_trigger event on manual trigger success
- All log_action calls wrapped in try/except for safety

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-12 23:44:00 -05:00
parent 50211d1853
commit 1a1ceb2cb1
4 changed files with 68 additions and 2 deletions

View File

@@ -161,10 +161,10 @@ Plans:
2. `config_snapshot_skipped_duplicate` audit event logged when a duplicate snapshot is detected
3. `config_diff_generated` audit event logged when a diff is created between snapshots
4. `config_backup_manual_trigger` audit event logged when an operator triggers a manual backup
**Plans**: TBD
**Plans**: 1 plan
Plans:
- [ ] 10-01: Audit event emission for all config backup operations
- [ ] 10-01-PLAN.md — Audit event emission for all config backup operations
## Progress

View File

@@ -837,6 +837,23 @@ async def trigger_config_snapshot(
reply_data = json.loads(reply.data)
if reply_data.get("status") == "success":
try:
from app.services.audit_service import log_action
await log_action(
db,
tenant_id,
current_user.user_id,
"config_backup_manual_trigger",
resource_type="config_snapshot",
device_id=device_id,
details={
"sha256_hash": reply_data.get("sha256_hash"),
"triggered_by": str(current_user.user_id),
},
ip_address=request.client.host if request.client else None,
)
except Exception:
pass
return {
"status": "success",
"sha256_hash": reply_data.get("sha256_hash"),

View File

@@ -162,6 +162,27 @@ async def generate_and_store_diff(
lines_removed,
)
try:
from app.services.audit_service import log_action
import uuid as _uuid
await log_action(
db=None,
tenant_id=_uuid.UUID(tenant_id),
user_id=None,
action="config_diff_generated",
resource_type="config_diff",
resource_id=str(diff_id),
device_id=_uuid.UUID(device_id),
details={
"old_snapshot_id": str(old_snapshot_id),
"new_snapshot_id": new_snapshot_id,
"lines_added": lines_added,
"lines_removed": lines_removed,
},
)
except Exception:
pass
# 11. Parse structured changes (best-effort)
try:
changes = parse_diff_changes(diff_text)

View File

@@ -11,6 +11,7 @@ import asyncio
import json
import logging
import time
import uuid as _uuid
from datetime import datetime, timezone
from typing import Any, Optional
@@ -20,6 +21,7 @@ from sqlalchemy.exc import IntegrityError, OperationalError
from app.config import settings
from app.database import AdminAsyncSessionLocal
from app.services.audit_service import log_action
from app.services.config_diff_service import generate_and_store_diff
from app.services.openbao_service import OpenBaoTransitService
@@ -111,6 +113,18 @@ async def handle_config_snapshot(msg) -> None:
device_id,
)
config_snapshot_dedup_skipped_total.inc()
try:
await log_action(
db=None,
tenant_id=_uuid.UUID(tenant_id),
user_id=None,
action="config_snapshot_skipped_duplicate",
resource_type="config_snapshot",
device_id=_uuid.UUID(device_id),
details={"sha256_hash": sha256_hash},
)
except Exception:
pass
await msg.ack()
return
@@ -173,6 +187,20 @@ async def handle_config_snapshot(msg) -> None:
await msg.nak()
return
try:
await log_action(
db=None,
tenant_id=_uuid.UUID(tenant_id),
user_id=None,
action="config_snapshot_created",
resource_type="config_snapshot",
resource_id=str(new_snapshot_id),
device_id=_uuid.UUID(device_id),
details={"sha256_hash": sha256_hash},
)
except Exception:
pass
# --- Diff generation (best-effort) ---
try:
await generate_and_store_diff(device_id, tenant_id, str(new_snapshot_id), session)