Skip to content

Commit d2bf617

Browse files
committed
RBAC: drop upfront UserRole inserts from org-creation and invite flows
The enterprise plugin's getUserRole now derives a user's role from the legacy public.OrgMember.role column whenever no explicit UserRole row exists (separate cloud commit). That makes the upfront UserRole writes in createOrganization and acceptInvite redundant — the role display and ability checks both work from day one based on OrgMember alone. Removed: - The rbac.setUserRole call + SYSTEM_ROLE_IDS import from apps/webapp/app/models/organization.server.ts (createOrganization) - The rbac.setUserRole call + SYSTEM_ROLE_IDS import from apps/webapp/app/models/member.server.ts (acceptInvite) A UserRole row is now only ever inserted when an Owner explicitly changes someone's role on the Teams page. Everyone else's role is derived live from OrgMember.role.
1 parent a29e8b7 commit d2bf617

2 files changed

Lines changed: 12 additions & 49 deletions

File tree

apps/webapp/app/models/member.server.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { type Prisma, prisma } from "~/db.server";
22
import { createEnvironment } from "./organization.server";
33
import { customAlphabet } from "nanoid";
44
import { logger } from "~/services/logger.server";
5-
import { rbac, SYSTEM_ROLE_IDS } from "~/services/rbac.server";
65

76
const tokenValueLength = 40;
87
const tokenGenerator = customAlphabet("123456789abcdefghijkmnopqrstuvwxyz", tokenValueLength);
@@ -216,32 +215,12 @@ export async function acceptInvite({
216215
};
217216
});
218217

219-
// 5. Assign the corresponding RBAC role for the new member. Done
220-
// outside the transaction because rbac runs against a separate
221-
// postgres-js connection (Drizzle, not Prisma) — calling it inside
222-
// the tx would mix transaction boundaries. The legacy OrgMember.role
223-
// → RBAC mapping matches the backfill migration (TRI-8854):
224-
// ADMIN → Owner
225-
// MEMBER → Member
226-
// In practice every invite is created with role=MEMBER (see
227-
// inviteMembers above — there's no UI to invite someone as ADMIN),
228-
// so the ADMIN branch is defensive cover for direct DB writes.
229-
// OSS fallback returns ok=false; we log + continue (legacy
230-
// OrgMember.role is the source of truth for OSS auth).
231-
const roleId =
232-
result.inviteRole === "ADMIN" ? SYSTEM_ROLE_IDS.owner : SYSTEM_ROLE_IDS.member;
233-
const roleResult = await rbac.setUserRole({
234-
userId: user.id,
235-
organizationId: result.organization.id,
236-
roleId,
237-
});
238-
if (!roleResult.ok) {
239-
logger.debug("acceptInvite: skipped RBAC role assignment", {
240-
organizationId: result.organization.id,
241-
userId: user.id,
242-
reason: roleResult.error,
243-
});
244-
}
218+
// No upfront RBAC UserRole insert — the enterprise plugin's
219+
// getUserRole derives the new member's role from the legacy
220+
// OrgMember.role write inside the transaction above (ADMIN → Owner,
221+
// MEMBER → Admin) until an Owner explicitly changes their role on
222+
// the Teams page. Keeps the invite path tight and consistent with
223+
// the create-org path's behaviour.
245224

246225
return { remainingInvites: result.remainingInvites, organization: result.organization };
247226
}

apps/webapp/app/models/organization.server.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import slug from "slug";
1212
import { prisma, type PrismaClientOrTransaction } from "~/db.server";
1313
import { env } from "~/env.server";
1414
import { featuresForUrl } from "~/features.server";
15-
import { logger } from "~/services/logger.server";
16-
import { rbac, SYSTEM_ROLE_IDS } from "~/services/rbac.server";
1715
import { createApiKeyForEnv, createPkApiKeyForEnv, envSlug } from "./api-key.server";
1816
import { getDefaultEnvironmentConcurrencyLimit } from "~/services/platform.v3.server";
1917
export type { Organization };
@@ -84,26 +82,12 @@ export async function createOrganization(
8482
},
8583
});
8684

87-
// Assign the creator the Owner system role so the new Teams page UI
88-
// shows them as an Owner from the moment the org exists. Mirrors the
89-
// legacy `OrgMember.role = "ADMIN"` write above (TRI-8854: legacy
90-
// ADMIN maps to new Owner, not new Admin — the new Admin role
91-
// excludes billing + member management). On the OSS deployment the
92-
// fallback's setUserRole returns ok=false and we just log; the legacy
93-
// OrgMember.role write is the source of truth for OSS auth.
94-
const roleResult = await rbac.setUserRole({
95-
userId,
96-
organizationId: organization.id,
97-
roleId: SYSTEM_ROLE_IDS.owner,
98-
});
99-
if (!roleResult.ok) {
100-
logger.debug("createOrganization: skipped RBAC role assignment", {
101-
organizationId: organization.id,
102-
userId,
103-
reason: roleResult.error,
104-
});
105-
}
106-
85+
// No upfront RBAC UserRole insert — the enterprise plugin's
86+
// getUserRole derives the creator's role from the legacy
87+
// OrgMember.role = "ADMIN" write above (which maps to Owner) until an
88+
// Owner explicitly changes someone's role on the Teams page. Keeps
89+
// the create-org path tight and lets the fallback path stay the
90+
// single source of truth for "who has what role" by default.
10791
return { ...organization };
10892
}
10993

0 commit comments

Comments
 (0)