Skip to content

fix(admin): delete workspaces on ban#4029

Merged
TheodoreSpeaks merged 3 commits intostagingfrom
fix/user-ban
Apr 8, 2026
Merged

fix(admin): delete workspaces on ban#4029
TheodoreSpeaks merged 3 commits intostagingfrom
fix/user-ban

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

@TheodoreSpeaks TheodoreSpeaks commented Apr 8, 2026

Summary

Banning a user currently doesn't delete their resources or api keys. Switched this to delete all workspaces and associated resources that they own. This cleans api keys, mcp servers, deployed workflows, etc.

Also fixed formatting with ban action buttons overlapping other admin buttons.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Tested in local that resources are deleted and deployed resources are undeployed

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 8, 2026 2:38am

Request Review

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@BugBot review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 8, 2026

PR Summary

Medium Risk
Automatically archiving all workspaces owned by a newly banned user (and deleting their API keys) is a destructive, cascading operation that runs on user updates, so misfires or partial failures could impact data availability and operational state.

Overview
Banning a user now triggers server-side cleanup: on user update, if user.banned is set, the system calls disableUserResources to archive every non-archived workspace the user owns (cascading to associated resources via workspace lifecycle) and delete the user’s personal API keys.

The admin user management UI is reworked to prevent action-button overlap by splitting each user row into a header row plus an inline “Confirm Ban” sub-row with an optional ban reason, and by toggling the Ban/Cancel state separately from the confirm action.

Reviewed by Cursor Bugbot for commit eb96a73. Bugbot is set up for automated code reviews on this repo. Configure here.

@TheodoreSpeaks TheodoreSpeaks marked this pull request as ready for review April 8, 2026 01:19
@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@greptile review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR implements resource cleanup when a user is banned via the admin panel. It adds a disableUserResources function in lib/workflows/lifecycle.ts that archives all workspaces owned by the banned user (cascading to workflows, chats, forms, MCP servers, webhooks, etc.) and deletes their personal API keys. The ban logic is hooked into Better Auth's databaseHooks.user.update.after callback. It also fixes a UI layout issue in the admin panel where ban action buttons were overlapping other buttons by moving to a two-row layout.

Key observations:

  • The update.after hook fires on every user record update, not just on the ban transition. Every time a banned user's record is updated, disableUserResources is called again.
  • The fire-and-forget pattern means there is a brief window between when a ban is applied and when resources are actually cleaned up.
  • Archiving owned workspaces cascades to all workspace members, not just the banned user.
  • The isGithubAuthDisabled / isGoogleAuthDisabled flag removal is a follow-up cleanup (those flags were already removed from feature-flags.ts) — not a behavioral change.

Confidence Score: 4/5

Safe to merge with caution — two P1 logic issues should be addressed before relying on this in production for multi-workspace users.

The core intent is correct and workspace archiving idempotency makes repeated invocations harmless in the happy path. However, Promise.all short-circuit on failure is a real reliability gap for users with multiple workspaces, and the hook firing on every user update (not just the ban transition) is a latent correctness issue.

apps/sim/lib/workflows/lifecycle.ts (Promise.all vs allSettled) and apps/sim/lib/auth/auth.ts (update.after triggering on every update, not just ban transition)

Vulnerabilities

  • The disableUserResources function is called fire-and-forget from the update.after hook, creating a brief window between ban application and resource disablement. Better Auth's ban mechanism should independently invalidate sessions during this window.
  • Promise.all rejection can leave workspaces active after a partial failure; active workspaces with deployed workflows or public APIs remain reachable during this window.
  • No auth or authorization check guards disableUserResources itself — it is an internal server-side function called only from the trusted auth hook, which is acceptable.
  • The removal of isGithubAuthDisabled / isGoogleAuthDisabled does not introduce new auth bypass risks as those flags were already removed elsewhere.

Important Files Changed

Filename Overview
apps/sim/lib/workflows/lifecycle.ts Adds disableUserResources which archives owned workspaces and deletes user API keys on ban; also removes archivedAt from ArchiveWorkflowOptions and strips folder-clearing logic from restoreWorkflow.
apps/sim/lib/auth/auth.ts Adds databaseHooks.user.update.after to fire disableUserResources when user.banned is truthy; removes isGithubAuthDisabled / isGoogleAuthDisabled guards (flags already removed from feature-flags.ts elsewhere).
apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx Refactors ban UI to a two-row layout to prevent button overflow; extracts the ban reason input and confirm button into a conditional second row below the user info row.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Admin bans user] --> B[BetterAuth updates user row]
    B --> C{update.after hook}
    C --> D{user.banned?}
    D -- No --> E[No-op]
    D -- Yes --> F[Call disableUserResources - fire and forget]
    B --> G[Ban confirmed to admin]

    F --> H[Query owned workspaces]
    H --> I[Promise.all]
    I --> J[archiveWorkspace for each]
    I --> K[Delete personal API keys]
    J --> L{Any promise rejected?}
    L -- Yes --> M[Outer catch logs error - remaining workspaces may stay active]
    L -- No --> N[All resources disabled]
Loading

Reviews (1): Last reviewed commit: "Fix lint" | Re-trigger Greptile

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eb96a73. Configure here.

@TheodoreSpeaks TheodoreSpeaks merged commit 712e58a into staging Apr 8, 2026
12 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the fix/user-ban branch April 8, 2026 03:21
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.

1 participant