Skip to content

feat(organization): add ownership transfer - #5236

Open
denis0wn wants to merge 1 commit into
Dokploy:canaryfrom
denis0wn:feat/transfer-ownership
Open

feat(organization): add ownership transfer#5236
denis0wn wants to merge 1 commit into
Dokploy:canaryfrom
denis0wn:feat/transfer-ownership

Conversation

@denis0wn

@denis0wn denis0wn commented Aug 31, 2026

Copy link
Copy Markdown

PR: feat(organization): add ownership transfer

Related issue: #1413 (checklist item "Add the ability to transfer ownership").
Slice announced in: #1413 (comment)

Description

Adds the ability to transfer organization ownership to an existing member. The
owner role was previously nontransferable by design (updateMemberRole rejects
any change to/from owner), so this introduces a dedicated, owner-only mutation
instead of relaxing that guard.

Changes

Backend — apps/dokploy/server/api/routers/organization.ts

  • New organization.transferOwnership mutation (memberId input):
    • Caller must be the organization owner (organization.ownerId or a member
      record with role === "owner" in the active organization); admins/members
      get FORBIDDEN.
    • Target must exist, belong to the active organization, and not be the caller;
      transferring to a member who is already the owner is rejected.
    • Atomic swap in a single transaction: target member role → owner, previous
      owner member role → admin, organization.ownerId → target user. This
      guarantees exactly one owner before and after the transfer.
    • Audit log entry with type: "transferOwnership", from/to/toEmail
      metadata (same audit pattern as the other organization mutations).

Frontend — apps/dokploy/components/dashboard/settings/users/show-users.tsx

  • "Transfer Ownership" action in the per-user dropdown, visible only to the
    organization owner, only for other members who are not owners.
  • Confirmation dialog (DialogAction, destructive) explaining that the caller
    becomes an admin; success/error toasts; refetches the member list and
    invalidates the session query so role-gated UI updates immediately.

Tests — apps/dokploy/__test__/api/organization-transfer-ownership.test.ts

10 focused tests using the existing mocked-DB harness:

  • zod input validation (missing/empty memberId);
  • unauthenticated calls rejected;
  • non-owner callers rejected;
  • missing organization / missing target member rejected;
  • cross-org target rejected;
  • self-transfer rejected;
  • target already owner rejected;
  • happy path asserts the transaction performs exactly three updates in order:
    member (new owner), member (previous owner demoted), organization (ownerId).

Verification performed by the author

  • pnpm exec vitest run --config __test__/vitest.config.ts (new file): 10/10 pass.
  • Full app test suite: 913 passed; the 10 failing tests are pre-existing on
    canary without this change (*.real.test.ts requiring a Docker socket and
    a git clone over https) — verified by stashing this branch and re-running them.
  • pnpm --filter=dokploy run typecheck (tsc --noEmit): pass.
  • pnpm exec biome check on the changed files: clean.
  • Note: a full local dev-environment walkthrough (dokploy:setup) was not run
    because this environment has no Docker daemon; the mutation is covered by the
    unit tests above and follows the exact patterns of the neighboring mutations.

AI assistance disclosure

This implementation was produced with an AI coding agent under the supervision
of the account owner, who reviewed the diff and the test results. All claims in
"Verification performed" are from actual runs.

Scope

Out of scope (separate slices of #1413): teams, view-only role, remote-server
assignment, team-based invitations.

Greptile Summary

Adds an owner-only organization ownership-transfer mutation, a dashboard confirmation action, and focused API tests.

  • Atomically updates the target role, previous-owner role, and organization owner identifier.
  • Refreshes member-related client state after a successful transfer.
  • Covers validation, authorization, organization scoping, and the sequential success path.

Confidence Score: 3/5

This PR should not merge until ownership transfers are serialized so concurrent requests cannot leave multiple users with owner privileges.

The new mutation checks ownership outside the transaction and has no database constraint or lock enforcing a single owner-role member, allowing concurrent transfers to persist multiple privileged owners.

Files Needing Attention: apps/dokploy/server/api/routers/organization.ts

Security Review

Concurrent transfers are not serialized, so separate targets can retain owner-role authorization after organization.ownerId resolves to only one of them.

Reviews (1): Last reviewed commit: "feat(organization): add ownership transf..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

Adds an owner-only organization.transferOwnership mutation and a
"Transfer Ownership" action in the Users table, covering the
"Add the ability to transfer ownership" item from Dokploy#1413.

- Target must be an existing member of the active organization;
  self-transfer and already-owner targets are rejected.
- Atomic swap in one transaction: target -> owner, previous owner
  -> admin, organization.ownerId updated; audit log entry included.
- UI gated to the organization owner, with a confirmation dialog and
  session/list invalidation after success.
- 10 focused vitest tests over the mocked-DB harness.
@denis0wn
denis0wn requested a review from Siumauricio as a code owner August 31, 2026 15:10
Comment thread apps/dokploy/server/api/routers/organization.ts
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