Skip to content

fix(profile): member delete must not destroy the shared account#52

Merged
paulocastellano merged 1 commit into
mainfrom
fix/profile-destroy-deletes-shared-account
May 19, 2026
Merged

fix(profile): member delete must not destroy the shared account#52
paulocastellano merged 1 commit into
mainfrom
fix/profile-destroy-deletes-shared-account

Conversation

@paulocastellano
Copy link
Copy Markdown
Contributor

Closes #50.

Bug

Members joining via workspace invite share the owner's account_id. `ProfileController::destroy` was unconditionally calling `$account->delete()` in every profile-deletion path, so any member could wipe the whole organization — workspaces, posts, social accounts, signatures, labels — just by clicking Delete on their own profile.

Fix

Gate the account/subscription teardown behind `isAccountOwner()`. For members the destroy path now only detaches them from workspaces and deletes the user row. Owner's data is untouched.

- if ($account) {
-     $account->delete();
- }
+ if ($account && $isOwner) {
+     if ($account->subscribed(Account::SUBSCRIPTION_NAME)) {
+         $account->subscription(Account::SUBSCRIPTION_NAME)->cancelNow();
+     }
+     $account->subscriptions()->delete();
+     $account->delete();
+ }

(Subscription cancellation moved into the same gate — it's an owner-only concern; for a member there's no subscription to touch.)

Tests

3 new tests, each parametrized over `SELF_HOSTED=true` and `false` (6 runs total):

  • Member deletes profile → shared account, workspace, owner all survive.
  • Member deletes profile → detached from workspace memberships.
  • Owner deletes profile → cascade still works (account + workspace gone).

Both modes pass because the path no longer depends on `self_hosted` — `isAccountOwner()` is a pure model check, and `$account->subscribed(...)` returns false on self-hosted (no Stripe records), so the cancel/delete-subscription calls naturally no-op.

Full suite: 1603 passing.

🤖 Generated with Claude Code

#50)

Members joining via workspace invite share the owner's account_id.
ProfileController::destroy was unconditionally calling $account->delete()
in every profile-deletion path, so any member could wipe the whole
organization (cascade: workspaces, posts, social accounts, signatures,
labels) just by clicking Delete on their own profile.

Gate the account/subscription teardown behind isAccountOwner(). For
members the path now only detaches them from workspaces and deletes
the user row — owner's data is untouched.

Tested in both SELF_HOSTED=true and false.
@paulocastellano paulocastellano merged commit 50315ac into main May 19, 2026
2 checks passed
@paulocastellano paulocastellano deleted the fix/profile-destroy-deletes-shared-account branch May 19, 2026 15:25
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.

[Bug] Critical: Deleting a member user destroys the entire account and all workspace data

1 participant