Audit trail

This commit is contained in:
2026-08-26 19:52:22 +02:00
parent ed76b35600
commit 314959c7bf
10 changed files with 103 additions and 13 deletions
+15 -6
View File
@@ -22,6 +22,7 @@ from backend.app.services.email_verification import create_verification_token
from backend.app.services.theme_service import THEMES, get_enabled_themes, save_enabled_themes
from backend.app.services.secret_store import encrypt_secret
from backend.app.core.config import settings
from backend.app.services.audit_service import record_audit_event
router = APIRouter()
@@ -95,7 +96,7 @@ def list_users(_: dict = Depends(require_admin)):
@router.post('/users/{user_id}/otp/reset')
def reset_user_otp(user_id: str, _: dict = Depends(require_admin)):
def reset_user_otp(user_id: str, current_user: dict = Depends(require_admin)):
with get_connection() as conn:
target = conn.execute('SELECT id FROM users WHERE id = ?', (user_id,)).fetchone()
if target is None:
@@ -106,11 +107,12 @@ def reset_user_otp(user_id: str, _: dict = Depends(require_admin)):
)
conn.execute('DELETE FROM otp_recovery_codes WHERE user_id = ?', (user_id,))
conn.commit()
record_audit_event(current_user['id'], 'otp_reset', 'user', user_id)
return {'status': 'otp_reset', 'enabled': False, 'user_id': user_id}
@router.post('/users', status_code=201)
def create_user(payload: AdminUserCreate, _: dict = Depends(require_admin)):
def create_user(payload: AdminUserCreate, current_user: dict = Depends(require_admin)):
username = payload.username.strip()
email = payload.email.strip()
if not username or not email or len(payload.password) < 8:
@@ -141,6 +143,7 @@ def create_user(payload: AdminUserCreate, _: dict = Depends(require_admin)):
send_verification_email(row['email'], row['username'], verification_url)
except Exception as error:
raise HTTPException(status_code=503, detail=f'User created but verification email could not be sent: {error}') from error
record_audit_event(current_user['id'], 'user_created', 'user', row['id'], details={'is_admin': bool(payload.is_admin)})
return public_user(row)
@@ -166,19 +169,21 @@ def get_admin_themes(_: dict = Depends(require_admin)):
@router.put('/themes')
def update_admin_themes(payload: AdminThemesUpdate, _: dict = Depends(require_admin)):
def update_admin_themes(payload: AdminThemesUpdate, current_user: dict = Depends(require_admin)):
try:
enabled = save_enabled_themes(payload.themes)
except ValueError as error:
raise HTTPException(status_code=422, detail=str(error)) from error
record_audit_event(current_user['id'], 'themes_updated', 'application', details={'themes': enabled})
return {'themes': THEMES, 'enabled': enabled}
@router.put('/smtp')
def update_admin_smtp_settings(payload: AdminSmtpUpdate, _: dict = Depends(require_admin)):
def update_admin_smtp_settings(payload: AdminSmtpUpdate, current_user: dict = Depends(require_admin)):
current = get_smtp_settings()
values = validate_smtp_values(payload, current)
save_smtp_settings(values)
record_audit_event(current_user['id'], 'smtp_settings_updated', 'application', details={'host': values['smtp_host'], 'port': values['smtp_port'], 'username': values['smtp_username'], 'tls': values['smtp_use_tls']})
return public_smtp_settings(values)
@@ -259,6 +264,7 @@ def update_user_privileges(
'SELECT id, username, email, is_admin, avatar_url, bio, created_at, email_verified FROM users WHERE id = ?',
(user_id,),
).fetchone()
record_audit_event(current_user['id'], 'user_privileges_updated', 'user', user_id, details={'is_admin': bool(payload.is_admin)})
return public_user(row)
@@ -281,13 +287,15 @@ def delete_user(user_id: str, current_user: dict = Depends(require_admin)):
conn.execute('DELETE FROM links WHERE user_id = ?', (user_id,))
conn.execute('DELETE FROM users WHERE id = ?', (user_id,))
conn.commit()
record_audit_event(current_user['id'], 'user_deleted', 'user', user_id)
return {'status': 'deleted', 'id': user_id}
@router.delete('/labels/{label_id}')
def admin_delete_label(label_id: str, _: dict = Depends(require_admin)):
def admin_delete_label(label_id: str, current_user: dict = Depends(require_admin)):
if not delete_label(label_id, is_admin=True):
raise HTTPException(status_code=404, detail='Label not found')
record_audit_event(current_user['id'], 'label_deleted', 'label', label_id)
return {'status': 'deleted', 'id': label_id}
@@ -346,7 +354,7 @@ def get_plugin(plugin_name: str, _: dict = Depends(require_admin)):
def update_plugin(
plugin_name: str,
payload: AdminPluginUpdate,
_: dict = Depends(require_admin),
current_user: dict = Depends(require_admin),
):
with get_connection() as conn:
current = conn.execute(
@@ -375,6 +383,7 @@ def update_plugin(
)
conn.commit()
record_audit_event(current_user['id'], 'plugin_updated', 'plugin', plugin_name, details={'enabled': enabled})
return {
'name': plugin_name,
'enabled': enabled,