fix(users): delete user no longer 500s (clear all FK references)
Deleting a user cleared only Asset/Vulnerability assignments, but users.id is referenced by four more tables — notification_logs, ai_reports, user_groups, audit_logs — so Postgres RESTRICT blocked the delete with a 500. Clear every FK first. Historical rows are NULLed, not deleted, so a removed user's notifications, AI reports and — critically — AUDIT LOG entries survive (a user deletion must not erase who-did-what). Group memberships are removed outright. Then expire the cached relationship state so the User.audit_logs delete-orphan cascade doesn't re-delete the rows we just detached.
This commit is contained in:
+19
-2
@@ -1171,11 +1171,28 @@ async def delete_user(
|
||||
|
||||
username = user.username
|
||||
|
||||
# Clear user assignments before deletion to avoid FK constraint errors
|
||||
# Every FK into `users` must be cleared first, or Postgres blocks the
|
||||
# delete (→ 500). Historical rows are NULLed, not deleted: notifications,
|
||||
# AI reports and especially the AUDIT LOG must survive so a deleted user's
|
||||
# actions stay on record (revision-proof). Group memberships are removed
|
||||
# outright (the assoc row has no meaning without the user).
|
||||
from app.models.asset import Asset
|
||||
from app.models.vulnerability import Vulnerability
|
||||
from app.models.notification_log import NotificationLog
|
||||
from app.models.ai_report import AIReport
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.group import user_groups
|
||||
|
||||
db.query(Asset).filter(Asset.assigned_user_id == user_id).update({"assigned_user_id": None})
|
||||
db.query(Vulnerability).filter(Vulnerability.assigned_user_id == user_id).update({"assigned_user_id": None})
|
||||
db.query(NotificationLog).filter(NotificationLog.user_id == user_id).update({"user_id": None})
|
||||
db.query(AIReport).filter(AIReport.created_by_id == user_id).update({"created_by_id": None})
|
||||
db.query(AuditLog).filter(AuditLog.user_id == user_id).update({"user_id": None})
|
||||
db.execute(user_groups.delete().where(user_groups.c.user_id == user_id))
|
||||
db.flush()
|
||||
# Drop cached relationship state so the User.audit_logs delete-orphan
|
||||
# cascade doesn't re-delete the rows we just detached above.
|
||||
db.expire(user)
|
||||
|
||||
db.delete(user)
|
||||
db.commit()
|
||||
@@ -1183,7 +1200,7 @@ async def delete_user(
|
||||
log_audit_event(
|
||||
db,
|
||||
AuditEventType.USER_DELETED,
|
||||
f"User deleted: {username}",
|
||||
f"User deleted: {username} (id {user_id})",
|
||||
user_id=current_user.id,
|
||||
request=request
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user