Skip to content

feat(db): block deleting an org that still has service users#1796

Merged
whoAbhishekSah merged 1 commit into
mainfrom
feat/serviceusers-org-delete-guard
Jul 23, 2026
Merged

feat(db): block deleting an org that still has service users#1796
whoAbhishekSah merged 1 commit into
mainfrom
feat/serviceusers-org-delete-guard

Conversation

@whoAbhishekSah

@whoAbhishekSah whoAbhishekSah commented Jul 23, 2026

Copy link
Copy Markdown
Member

What

Adds a BEFORE DELETE trigger on organizations. If any serviceusers row still points at the org, the delete fails.

Why

Deleting an org used to leave its service users behind, with an org_id pointing at a row that no longer exists. Those leftover accounts kept authenticating. The app deleter now removes service users before the org, but the database had no fallback. This trigger is that fallback: if the app-level cleanup ever breaks, or a row is removed out of band, the org delete fails loudly instead of silently orphaning service users.

Normal deletes are unaffected — the deleter clears the service users first, so by the time the org row is deleted, the trigger has nothing to block.

Why a trigger and not a foreign key

The obvious fix is a foreign key on serviceusers.org_id with ON DELETE RESTRICT. That does not work here.

The bootstrap superuser service account lives under the virtual platform org (00000000-0000-0000-0000-000000000000). That org id is never inserted into the organizations table by design. A foreign key checks every row, so it would reject the bootstrap account and also fail on every server boot, which re-creates that account.

A trigger only runs when a real org is deleted. The platform org is never deleted, so the bootstrap account is never in scope. Same "fail loud" behaviour, no conflict with the special org id.

Testing

Verified locally against a running server:

  • Service user authenticates before the org is deleted → 200.
  • Raw DELETE FROM organizations while a service user still exists → blocked by the trigger.
  • API DeleteOrganization → succeeds, and the service user rows are gone.
  • Service user auth after the delete → 401.
  • Server restart with the trigger in place → boots fine, bootstrap account re-created as normal.

🤖 Generated with Claude Code

Adds a BEFORE DELETE trigger on organizations that fails if any
serviceusers row still points at the org. This is a DB-level safety
net: the app deleter already removes service users before the org, so
normal deletes are unaffected. If that cleanup ever regresses, or an
org is deleted out of band, the delete now fails loudly instead of
leaving service users that keep authenticating.

A plain foreign key can't be used. The bootstrap superuser lives under
the virtual platform org (uuid.Nil), which has no row in organizations
by design, so an FK would reject that row. A trigger only fires on real
org deletes, so the platform org and its bootstrap account stay out of
scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jul 23, 2026 8:55am

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Prevented organizations from being deleted while service users are still associated with them.
    • Improved database behavior by safely removing this restriction during rollback when applicable.

Walkthrough

Adds a PostgreSQL trigger preventing deletion of organizations referenced by service users, with a down migration that removes the trigger and function.

Changes

Organization delete protection

Layer / File(s) Summary
Organization delete trigger lifecycle
internal/store/postgres/migrations/20260723120000_restrict_org_delete_with_serviceusers.*.sql
The up migration creates a BEFORE DELETE trigger that raises foreign_key_violation for referenced organizations; the down migration removes the trigger and function with guarded cleanup.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 29992856414

Coverage remained the same at 46.629%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 38894
Covered Lines: 18136
Line Coverage: 46.63%
Coverage Strength: 14.19 hits per line

💛 - Coveralls

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a015a5f-b611-4bca-841e-72f29dd3932c

📥 Commits

Reviewing files that changed from the base of the PR and between 9e063e3 and 10fee4b.

📒 Files selected for processing (2)
  • internal/store/postgres/migrations/20260723120000_restrict_org_delete_with_serviceusers.down.sql
  • internal/store/postgres/migrations/20260723120000_restrict_org_delete_with_serviceusers.up.sql

@whoAbhishekSah

Copy link
Copy Markdown
Member Author

part of #1585

@whoAbhishekSah
whoAbhishekSah merged commit b831544 into main Jul 23, 2026
8 checks passed
@whoAbhishekSah
whoAbhishekSah deleted the feat/serviceusers-org-delete-guard branch July 23, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants