feat(org): add teams, viewer access and ownership transfer - #5492
Open
tokenjunkielabs wants to merge 14 commits into
Open
tokenjunkielabs wants to merge 14 commits into
tokenjunkielabs wants to merge 14 commits into
Conversation
Author
|
/claim #1413
…On Sun, 20 Sep 2026 20:28:18 -0700, "greptile-apps[bot]" ***@***.***> wrote:
@greptile-apps[bot] commented on this pull request.
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + teams: protectedProcedure.query(async ({ ctx }) => {
+ const orgId = ctx.session.activeOrganizationId;
+ return await db.query.team.findMany({
+ where: eq(team.organizationId, orgId),
+ with: {
+ members: {
+ with: { user: true },
+ },
[P1](#) [security](#) Team query exposes user data
Any authenticated organization member, including a viewer, can call this protectedProcedure. Because user: true returns the complete user record, the response exposes license keys, Stripe identifiers, ban details, trusted origins, and enterprise-license state. The comparable member-list endpoint requires member.read; this query should enforce equivalent access and return only the fields its callers need.
How this was verified: The endpoint has no role check and its relation directly exposes all columns declared on the user table to an authenticated organization member.
Knowledge Base Used:
- [API boundary](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/api-boundary.md)
- [Access and commercial controls](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/access-and-commercial-controls.md)
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + .update(organization)
+ .set({ ownerId: target.userId })
+ .where(eq(organization.id, orgId));
[P1](#) Transfer drops organization entitlements
Ownership transfer changes organization.ownerId without moving or preserving the organization's owner-bound billing and enterprise entitlements. When the selected member lacks the former owner's Stripe customer or license fields, plan and license checks immediately resolve against the new owner and return no paid access. The organization therefore loses its paid or enterprise features even though the existing subscription or license remains attached to the former owner.
Knowledge Base Used:
- [Access and commercial controls](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/access-and-commercial-controls.md)
- [Identity, permissions, and audit](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/identity-permissions-and-audit.md)
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + let targetTeam: typeof team.$inferSelect | undefined;
+ if (input.teamId) {
+ targetTeam = await db.query.team.findFirst({
+ where: and(eq(team.id, input.teamId), eq(team.organizationId, orgId)),
+ });
+ if (!targetTeam) {
+ throw new TRPCError({ code: "NOT_FOUND", message: "Team not found" });
+ }
+ if (targetTeam.memberCount >= targetTeam.maxMembers) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Team member limit reached",
+ });
+ }
[P1](#) Team capacity check races
The code reads and checks memberCount before starting the membership transaction, and the database has no constraint or conditional update enforcing memberCount < maxMembers. If two administrators move different members into a team with one remaining slot at the same time, both requests can pass this check and increment the count, leaving the team above its configured limit. Capacity needs to be claimed atomically inside the transaction.
—
Reply to this email directly, [view it on GitHub](#5492?email_source=notifications&email_token=CKG5U25ZLHLZBRDGQXCGW5T5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#pullrequestreview-5262826096), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/CKG5U257YSEJAZYVOVXYJF35QCN5FAVCNFSNUABFKJSXA33TNF2G64TZHM3TQOBYGQ3TSMZXHNEXG43VMU5TKNJSGIZTMMRVGUYKC5QC).
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for [iOS](https://github.com/notifications/mobile/ios/CKG5U27JGL7O3WKJ7F7BPQD5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG) and [Android](https://github.com/notifications/mobile/android/CKG5U23EWRIDTPPKUPNPTZL5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA). Download it today!
You are receiving this because you authored the thread.
|
… ownership transfer
Author
|
The P1 organization-router repair is now on the existing claimant
branch for PR #5492. The updated
`apps/dokploy/server/api/routers/organization.ts` is blob
`cdc40831571bdc18af9611fbe38dc3902714b63b` on
`tokenjunkielabs:zz-riftheron/dokploy-1413-org-teams`, replacing the
reviewed `80c4d4bc6a2f9b93949396cbc3c06e32a6f8288f` version. Please
re-review the current PR head for the team-move atomicity and
ownership-transfer findings.
…On Sun, 20 Sep 2026 20:28:18 -0700, "greptile-apps[bot]" ***@***.***> wrote:
@greptile-apps[bot] commented on this pull request.
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + teams: protectedProcedure.query(async ({ ctx }) => {
+ const orgId = ctx.session.activeOrganizationId;
+ return await db.query.team.findMany({
+ where: eq(team.organizationId, orgId),
+ with: {
+ members: {
+ with: { user: true },
+ },
[P1](#) [security](#) Team query exposes user data
Any authenticated organization member, including a viewer, can call this protectedProcedure. Because user: true returns the complete user record, the response exposes license keys, Stripe identifiers, ban details, trusted origins, and enterprise-license state. The comparable member-list endpoint requires member.read; this query should enforce equivalent access and return only the fields its callers need.
How this was verified: The endpoint has no role check and its relation directly exposes all columns declared on the user table to an authenticated organization member.
Knowledge Base Used:
- [API boundary](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/api-boundary.md)
- [Access and commercial controls](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/access-and-commercial-controls.md)
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + .update(organization)
+ .set({ ownerId: target.userId })
+ .where(eq(organization.id, orgId));
[P1](#) Transfer drops organization entitlements
Ownership transfer changes organization.ownerId without moving or preserving the organization's owner-bound billing and enterprise entitlements. When the selected member lacks the former owner's Stripe customer or license fields, plan and license checks immediately resolve against the new owner and return no paid access. The organization therefore loses its paid or enterprise features even though the existing subscription or license remains attached to the former owner.
Knowledge Base Used:
- [Access and commercial controls](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/access-and-commercial-controls.md)
- [Identity, permissions, and audit](https://app.greptile.com/dokploy/-/custom-context/knowledge-base/dokploy/dokploy/-/docs/identity-permissions-and-audit.md)
In [apps/dokploy/server/api/routers/organization.ts](#5492 (comment)):
> + let targetTeam: typeof team.$inferSelect | undefined;
+ if (input.teamId) {
+ targetTeam = await db.query.team.findFirst({
+ where: and(eq(team.id, input.teamId), eq(team.organizationId, orgId)),
+ });
+ if (!targetTeam) {
+ throw new TRPCError({ code: "NOT_FOUND", message: "Team not found" });
+ }
+ if (targetTeam.memberCount >= targetTeam.maxMembers) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Team member limit reached",
+ });
+ }
[P1](#) Team capacity check races
The code reads and checks memberCount before starting the membership transaction, and the database has no constraint or conditional update enforcing memberCount < maxMembers. If two administrators move different members into a team with one remaining slot at the same time, both requests can pass this check and increment the count, leaving the team above its configured limit. Capacity needs to be claimed atomically inside the transaction.
—
Reply to this email directly, [view it on GitHub](#5492?email_source=notifications&email_token=CKG5U25ZLHLZBRDGQXCGW5T5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#pullrequestreview-5262826096), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/CKG5U257YSEJAZYVOVXYJF35QCN5FAVCNFSNUABFKJSXA33TNF2G64TZHM3TQOBYGQ3TSMZXHNEXG43VMU5TKNJSGIZTMMRVGUYKC5QC).
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for [iOS](https://github.com/notifications/mobile/ios/CKG5U27JGL7O3WKJ7F7BPQD5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG) and [Android](https://github.com/notifications/mobile/android/CKG5U23EWRIDTPPKUPNPTZL5QCN5FA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMRWGI4DENRQHE3KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA). Download it today!
You are receiving this because you authored the thread.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
C:/Program Files/Git/claim #1413
Closes #1413
This PR is not safe to merge until team membership data is properly authorized and minimized, ownership transfer preserves paid entitlements, and team limits are enforced atomically.
Summary
This PR adds organization teams, a read-only viewer role, team-based resource permissions, deployment-management overrides, organization descriptions, and owner transfer.
Reviews (1) · Last reviewed commit: "feat(org): add teams, viewer access and ..."