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
+9
View File
@@ -19,6 +19,7 @@ from backend.app.services.email_addresses import add_user_email_address, create_
from backend.app.services.email_service import send_verification_email, smtp_configured
from backend.app.core.config import settings
from backend.app.services.secret_store import decrypt_secret, encrypt_secret
from backend.app.services.audit_service import record_audit_event
router = APIRouter()
@@ -116,6 +117,7 @@ def update_password(payload: PasswordUpdate, user: dict = Depends(get_current_us
(hash_password(payload.new_password), user['id']),
)
conn.commit()
record_audit_event(user['id'], 'password_changed', 'user', user['id'])
return {'status': 'password_updated'}
@@ -132,6 +134,7 @@ def setup_otp(user: dict = Depends(get_current_user)):
with get_connection() as conn:
conn.execute('UPDATE users SET otp_secret = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', (encrypt_secret(secret), user['id']))
conn.commit()
record_audit_event(user['id'], 'otp_enrolled', 'user', user['id'])
return {
'secret': secret,
'otpauth_url': provisioning_uri(secret, user['username']),
@@ -157,6 +160,7 @@ def update_otp(payload: OtpUpdate, user: dict = Depends(get_current_user)):
else:
conn.execute('UPDATE users SET otp_enabled = 0, otp_secret = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?', (user['id'],))
conn.commit()
record_audit_event(user['id'], f'otp_{payload.action}d', 'user', user['id'])
return {'status': 'updated', 'enabled': payload.action == 'enable'}
@@ -172,6 +176,7 @@ def recover_otp(payload: OtpRecovery, user: dict = Depends(get_current_user)):
(user['id'],),
)
conn.commit()
record_audit_event(user['id'], 'otp_recovered', 'user', user['id'])
return {'status': 'otp_recovered', 'enabled': False}
@@ -259,6 +264,7 @@ def remove_additional_email(address_id: str, user: dict = Depends(get_current_us
conn.commit()
if cursor.rowcount == 0:
raise HTTPException(status_code=404, detail='Email address not found')
record_audit_event(user['id'], 'email_address_deleted', 'email_address', address_id)
return {'status': 'deleted', 'id': address_id}
@@ -287,6 +293,7 @@ def make_email_primary(address_id: str, user: dict = Depends(get_current_user)):
(address['email'], user['id']),
)
conn.commit()
record_audit_event(user['id'], 'primary_email_changed', 'user', user['id'])
return {'status': 'updated', 'email': address['email']}
@@ -318,6 +325,7 @@ def edit_label(label_id: str, payload: LabelUpdate, user: dict = Depends(get_cur
def remove_label(label_id: str, user: dict = Depends(get_current_user)):
if not delete_label(label_id, user['id']):
raise HTTPException(status_code=404, detail='Label not found or not owned by user')
record_audit_event(user['id'], 'label_deleted', 'label', label_id)
return {'status': 'deleted', 'id': label_id}
@@ -361,6 +369,7 @@ async def upload_avatar(
(avatar_url, user['id']),
)
conn.commit()
record_audit_event(user['id'], 'avatar_updated', 'user', user['id'])
return {'avatar_url': avatar_url}