Skip to content

Commit 4f48b2f

Browse files
committed
address more comments
1 parent 3283f60 commit 4f48b2f

7 files changed

Lines changed: 67 additions & 55 deletions

File tree

apps/sim/app/api/organizations/[id]/invitations/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ export const POST = withRouteHandler(
9494
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
9595
}
9696

97-
await validateInvitationsAllowed(session.user.id)
98-
9997
const { id: organizationId } = await params
98+
99+
await validateInvitationsAllowed(session.user.id, { organizationId })
100+
100101
const url = new URL(request.url)
101102
const validateOnly = url.searchParams.get('validate') === 'true'
102103
const isBatch = url.searchParams.get('batch') === 'true'

apps/sim/app/api/organizations/[id]/members/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,10 @@ export const POST = withRouteHandler(
165165
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
166166
}
167167

168-
await validateInvitationsAllowed(session.user.id)
169-
170168
const { id: organizationId } = await params
169+
170+
await validateInvitationsAllowed(session.user.id, { organizationId })
171+
171172
const { email, role = 'member' } = await request.json()
172173

173174
if (!email) {

apps/sim/app/api/workspaces/[workspaceId]/permission-groups/[id]/members/bulk/route.ts renamed to apps/sim/app/api/workspaces/[id]/permission-groups/[groupId]/members/bulk/route.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,13 @@ const bulkAddSchema = z.object({
3535
})
3636

3737
export const POST = withRouteHandler(
38-
async (
39-
req: NextRequest,
40-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
41-
) => {
38+
async (req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
4239
const session = await getSession()
4340
if (!session?.user?.id) {
4441
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
4542
}
4643

47-
const { workspaceId, id } = await params
44+
const { id: workspaceId, groupId: id } = await params
4845

4946
try {
5047
const isWorkspaceAdmin = await hasWorkspaceAdminAccess(session.user.id, workspaceId)

apps/sim/app/api/workspaces/[workspaceId]/permission-groups/[id]/members/route.ts renamed to apps/sim/app/api/workspaces/[id]/permission-groups/[groupId]/members/route.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,13 @@ async function loadGroupInWorkspace(groupId: string, workspaceId: string) {
3030
}
3131

3232
export const GET = withRouteHandler(
33-
async (
34-
_req: NextRequest,
35-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
36-
) => {
33+
async (_req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
3734
const session = await getSession()
3835
if (!session?.user?.id) {
3936
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
4037
}
4138

42-
const { workspaceId, id } = await params
39+
const { id: workspaceId, groupId: id } = await params
4340

4441
const access = await checkWorkspaceAccess(workspaceId, session.user.id)
4542
if (!access.exists) {
@@ -84,16 +81,13 @@ const addMemberSchema = z.object({
8481
})
8582

8683
export const POST = withRouteHandler(
87-
async (
88-
req: NextRequest,
89-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
90-
) => {
84+
async (req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
9185
const session = await getSession()
9286
if (!session?.user?.id) {
9387
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
9488
}
9589

96-
const { workspaceId, id } = await params
90+
const { id: workspaceId, groupId: id } = await params
9791

9892
try {
9993
const isWorkspaceAdmin = await hasWorkspaceAdminAccess(session.user.id, workspaceId)
@@ -242,16 +236,13 @@ export const POST = withRouteHandler(
242236
)
243237

244238
export const DELETE = withRouteHandler(
245-
async (
246-
req: NextRequest,
247-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
248-
) => {
239+
async (req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
249240
const session = await getSession()
250241
if (!session?.user?.id) {
251242
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
252243
}
253244

254-
const { workspaceId, id } = await params
245+
const { id: workspaceId, groupId: id } = await params
255246
const { searchParams } = new URL(req.url)
256247
const memberId = searchParams.get('memberId')
257248

apps/sim/app/api/workspaces/[workspaceId]/permission-groups/[id]/route.ts renamed to apps/sim/app/api/workspaces/[id]/permission-groups/[groupId]/route.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,13 @@ async function loadGroupInWorkspace(groupId: string, workspaceId: string) {
4545
}
4646

4747
export const GET = withRouteHandler(
48-
async (
49-
_req: NextRequest,
50-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
51-
) => {
48+
async (_req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
5249
const session = await getSession()
5350
if (!session?.user?.id) {
5451
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
5552
}
5653

57-
const { workspaceId, id } = await params
54+
const { id: workspaceId, groupId: id } = await params
5855

5956
const access = await checkWorkspaceAccess(workspaceId, session.user.id)
6057
if (!access.exists) {
@@ -88,16 +85,13 @@ export const GET = withRouteHandler(
8885
)
8986

9087
export const PUT = withRouteHandler(
91-
async (
92-
req: NextRequest,
93-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
94-
) => {
88+
async (req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
9589
const session = await getSession()
9690
if (!session?.user?.id) {
9791
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
9892
}
9993

100-
const { workspaceId, id } = await params
94+
const { id: workspaceId, groupId: id } = await params
10195

10296
try {
10397
const isWorkspaceAdmin = await hasWorkspaceAdminAccess(session.user.id, workspaceId)
@@ -217,16 +211,13 @@ export const PUT = withRouteHandler(
217211
)
218212

219213
export const DELETE = withRouteHandler(
220-
async (
221-
req: NextRequest,
222-
{ params }: { params: Promise<{ workspaceId: string; id: string }> }
223-
) => {
214+
async (req: NextRequest, { params }: { params: Promise<{ id: string; groupId: string }> }) => {
224215
const session = await getSession()
225216
if (!session?.user?.id) {
226217
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
227218
}
228219

229-
const { workspaceId, id } = await params
220+
const { id: workspaceId, groupId: id } = await params
230221

231222
try {
232223
const isWorkspaceAdmin = await hasWorkspaceAdminAccess(session.user.id, workspaceId)

apps/sim/app/api/workspaces/[workspaceId]/permission-groups/route.ts renamed to apps/sim/app/api/workspaces/[id]/permission-groups/route.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ const createSchema = z.object({
2727
})
2828

2929
export const GET = withRouteHandler(
30-
async (_req: NextRequest, { params }: { params: Promise<{ workspaceId: string }> }) => {
30+
async (_req: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
3131
const session = await getSession()
3232
if (!session?.user?.id) {
3333
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
3434
}
3535

36-
const { workspaceId } = await params
36+
const { id: workspaceId } = await params
3737

3838
const access = await checkWorkspaceAccess(workspaceId, session.user.id)
3939
if (!access.exists) {
@@ -89,13 +89,13 @@ export const GET = withRouteHandler(
8989
)
9090

9191
export const POST = withRouteHandler(
92-
async (req: NextRequest, { params }: { params: Promise<{ workspaceId: string }> }) => {
92+
async (req: NextRequest, { params }: { params: Promise<{ id: string }> }) => {
9393
const session = await getSession()
9494
if (!session?.user?.id) {
9595
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
9696
}
9797

98-
const { workspaceId } = await params
98+
const { id: workspaceId } = await params
9999

100100
try {
101101
const isWorkspaceAdmin = await hasWorkspaceAdminAccess(session.user.id, workspaceId)

apps/sim/ee/access-control/utils/permission-check.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '@sim/db'
2-
import { permissionGroup, permissionGroupMember } from '@sim/db/schema'
2+
import { permissionGroup, permissionGroupMember, workspace } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
4-
import { and, asc, eq } from 'drizzle-orm'
4+
import { and, asc, eq, sql } from 'drizzle-orm'
55
import { isWorkspaceOnEnterprisePlan } from '@/lib/billing'
66
import {
77
getAllowedIntegrationsFromEnv,
@@ -296,32 +296,63 @@ export async function validateSkillsAllowed(
296296
}
297297

298298
/**
299-
* Validates if the user is allowed to send invitations for the given workspace.
300-
* Also checks the global feature flag. When `workspaceId` is omitted only the
301-
* feature-flag check runs (no permission-group gate).
299+
* Validates if the user is allowed to send invitations. Pass one of:
300+
* - `workspaceId` — workspace-scoped invite: block when the user's group in that workspace has
301+
* `disableInvitations`.
302+
* - `organizationId` — organization-level invite (no specific workspace target): block when the
303+
* user has `disableInvitations` set on their group in any organization-owned workspace. This
304+
* mirrors the pre-refactor behavior where `disableInvitations` was an organization-level
305+
* policy.
306+
* - neither — only the global feature flag is checked.
302307
*/
303308
export async function validateInvitationsAllowed(
304309
userId: string | undefined,
305-
workspaceId?: string
310+
scope: string | { workspaceId?: string; organizationId?: string } = {}
306311
): Promise<void> {
307312
if (isInvitationsDisabled) {
308313
logger.warn('Invitations blocked by feature flag')
309314
throw new InvitationsNotAllowedError()
310315
}
311316

312-
if (!userId || !workspaceId) {
317+
if (!userId) {
313318
return
314319
}
315320

316-
const config = await getUserPermissionConfig(userId, workspaceId)
321+
const { workspaceId, organizationId } =
322+
typeof scope === 'string' ? { workspaceId: scope, organizationId: undefined } : scope
317323

318-
if (!config) {
324+
if (workspaceId) {
325+
const config = await getUserPermissionConfig(userId, workspaceId)
326+
if (config?.disableInvitations) {
327+
logger.warn('Invitations blocked by permission group', { userId, workspaceId })
328+
throw new InvitationsNotAllowedError()
329+
}
319330
return
320331
}
321332

322-
if (config.disableInvitations) {
323-
logger.warn('Invitations blocked by permission group', { userId, workspaceId })
324-
throw new InvitationsNotAllowedError()
333+
if (organizationId) {
334+
const [row] = await db
335+
.select({ id: permissionGroup.id })
336+
.from(permissionGroupMember)
337+
.innerJoin(permissionGroup, eq(permissionGroupMember.permissionGroupId, permissionGroup.id))
338+
.innerJoin(workspace, eq(permissionGroup.workspaceId, workspace.id))
339+
.where(
340+
and(
341+
eq(permissionGroupMember.userId, userId),
342+
eq(workspace.organizationId, organizationId),
343+
sql`${permissionGroup.config} @> '{"disableInvitations": true}'::jsonb`
344+
)
345+
)
346+
.limit(1)
347+
348+
if (row) {
349+
logger.warn('Invitations blocked by permission group (organization-wide)', {
350+
userId,
351+
organizationId,
352+
permissionGroupId: row.id,
353+
})
354+
throw new InvitationsNotAllowedError()
355+
}
325356
}
326357
}
327358

0 commit comments

Comments
 (0)