Skip to content

Commit 6cbc5c7

Browse files
committed
fix edge cases
1 parent 979f096 commit 6cbc5c7

12 files changed

Lines changed: 111 additions & 63 deletions

File tree

apps/sim/app/api/guardrails/validate/route.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { validateHallucination } from '@/lib/guardrails/validate_hallucination'
66
import { validateJson } from '@/lib/guardrails/validate_json'
77
import { validatePII } from '@/lib/guardrails/validate_pii'
88
import { validateRegex } from '@/lib/guardrails/validate_regex'
9+
import {
10+
assertPermissionsAllowed,
11+
ProviderNotAllowedError,
12+
} from '@/ee/access-control/utils/permission-check'
913

1014
const logger = createLogger('GuardrailsValidateAPI')
1115

@@ -110,9 +114,6 @@ export async function POST(request: NextRequest) {
110114
}
111115

112116
if (validationType === 'hallucination' && model && workspaceId) {
113-
const { assertPermissionsAllowed, ProviderNotAllowedError } = await import(
114-
'@/ee/access-control/utils/permission-check'
115-
)
116117
try {
117118
await assertPermissionsAllowed({
118119
userId: auth.userId,

apps/sim/app/api/mcp/tools/execute/route.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import {
1313
createMcpSuccessResponse,
1414
validateStringParam,
1515
} from '@/lib/mcp/utils'
16+
import {
17+
assertPermissionsAllowed,
18+
McpToolsNotAllowedError,
19+
} from '@/ee/access-control/utils/permission-check'
1620

1721
const logger = createLogger('McpToolExecutionAPI')
1822

@@ -71,6 +75,19 @@ export const POST = withMcpAuth('read')(
7175
return createMcpErrorResponse(new Error(toolNameValidation.error), 'Invalid toolName', 400)
7276
}
7377

78+
try {
79+
await assertPermissionsAllowed({
80+
userId,
81+
workspaceId,
82+
toolKind: 'mcp',
83+
})
84+
} catch (err) {
85+
if (err instanceof McpToolsNotAllowedError) {
86+
return createMcpErrorResponse(err, err.message, 403)
87+
}
88+
throw err
89+
}
90+
7491
logger.info(
7592
`[${requestId}] Executing tool ${toolName} on server ${serverId} for user ${userId} in workspace ${workspaceId}`
7693
)

apps/sim/app/api/permission-groups/user/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export async function GET(req: Request) {
3434
permissionGroupId: null,
3535
groupName: null,
3636
config: null,
37+
entitled: false,
3738
})
3839
}
3940

@@ -59,12 +60,14 @@ export async function GET(req: Request) {
5960
permissionGroupId: null,
6061
groupName: null,
6162
config: null,
63+
entitled: true,
6264
})
6365
}
6466

6567
return NextResponse.json({
6668
permissionGroupId: groupMembership.permissionGroupId,
6769
groupName: groupMembership.groupName,
6870
config: parsePermissionGroupConfig(groupMembership.config),
71+
entitled: true,
6972
})
7073
}

apps/sim/app/api/providers/route.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import {
1212
refreshTokenIfNeeded,
1313
resolveOAuthAccountId,
1414
} from '@/app/api/auth/oauth/utils'
15+
import {
16+
assertPermissionsAllowed,
17+
IntegrationNotAllowedError,
18+
ProviderNotAllowedError,
19+
} from '@/ee/access-control/utils/permission-check'
1520
import type { StreamingExecution } from '@/executor/types'
1621
import { executeProviderRequest } from '@/providers'
1722

@@ -103,8 +108,6 @@ export async function POST(request: NextRequest) {
103108
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
104109
}
105110

106-
const { assertPermissionsAllowed, IntegrationNotAllowedError, ProviderNotAllowedError } =
107-
await import('@/ee/access-control/utils/permission-check')
108111
try {
109112
await assertPermissionsAllowed({
110113
userId: auth.userId,

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { type NextRequest, NextResponse } from 'next/server'
77
import { z } from 'zod'
88
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
99
import { getSession } from '@/lib/auth'
10-
import { hasWorkspaceAccessControlAccess } from '@/lib/billing'
10+
import { hasWorkspaceAccessControlAccess, isWorkspaceOnEnterprisePlan } from '@/lib/billing'
1111
import { checkWorkspaceAccess, hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils'
1212

1313
const logger = createLogger('WorkspacePermissionGroupMembers')
@@ -45,6 +45,11 @@ export async function GET(
4545
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
4646
}
4747

48+
const entitled = await isWorkspaceOnEnterprisePlan(workspaceId)
49+
if (!entitled) {
50+
return NextResponse.json({ error: 'Access Control is an Enterprise feature' }, { status: 403 })
51+
}
52+
4853
const group = await loadGroupInWorkspace(id, workspaceId)
4954
if (!group) {
5055
return NextResponse.json({ error: 'Permission group not found' }, { status: 404 })

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

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,20 @@ import { type NextRequest, NextResponse } from 'next/server'
66
import { z } from 'zod'
77
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
88
import { getSession } from '@/lib/auth'
9-
import { hasWorkspaceAccessControlAccess } from '@/lib/billing'
9+
import { hasWorkspaceAccessControlAccess, isWorkspaceOnEnterprisePlan } from '@/lib/billing'
1010
import {
1111
type PermissionGroupConfig,
1212
parsePermissionGroupConfig,
13+
permissionGroupConfigSchema,
1314
} from '@/lib/permission-groups/types'
1415
import { checkWorkspaceAccess, hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils'
1516

1617
const logger = createLogger('WorkspacePermissionGroup')
1718

18-
const configSchema = z.object({
19-
allowedIntegrations: z.array(z.string()).nullable().optional(),
20-
allowedModelProviders: z.array(z.string()).nullable().optional(),
21-
hideTraceSpans: z.boolean().optional(),
22-
hideKnowledgeBaseTab: z.boolean().optional(),
23-
hideTablesTab: z.boolean().optional(),
24-
hideCopilot: z.boolean().optional(),
25-
hideIntegrationsTab: z.boolean().optional(),
26-
hideSecretsTab: z.boolean().optional(),
27-
hideApiKeysTab: z.boolean().optional(),
28-
hideInboxTab: z.boolean().optional(),
29-
hideFilesTab: z.boolean().optional(),
30-
disableMcpTools: z.boolean().optional(),
31-
disableCustomTools: z.boolean().optional(),
32-
disableSkills: z.boolean().optional(),
33-
disableInvitations: z.boolean().optional(),
34-
disablePublicApi: z.boolean().optional(),
35-
hideDeployApi: z.boolean().optional(),
36-
hideDeployMcp: z.boolean().optional(),
37-
hideDeployA2a: z.boolean().optional(),
38-
hideDeployChatbot: z.boolean().optional(),
39-
hideDeployTemplate: z.boolean().optional(),
40-
})
41-
4219
const updateSchema = z.object({
4320
name: z.string().trim().min(1).max(100).optional(),
4421
description: z.string().max(500).nullable().optional(),
45-
config: configSchema.optional(),
22+
config: permissionGroupConfigSchema.optional(),
4623
autoAddNewMembers: z.boolean().optional(),
4724
})
4825

@@ -85,6 +62,11 @@ export async function GET(
8562
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
8663
}
8764

65+
const entitled = await isWorkspaceOnEnterprisePlan(workspaceId)
66+
if (!entitled) {
67+
return NextResponse.json({ error: 'Access Control is an Enterprise feature' }, { status: 403 })
68+
}
69+
8870
const group = await loadGroupInWorkspace(id, workspaceId)
8971

9072
if (!group) {

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

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,21 @@ import { type NextRequest, NextResponse } from 'next/server'
77
import { z } from 'zod'
88
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
99
import { getSession } from '@/lib/auth'
10-
import { hasWorkspaceAccessControlAccess } from '@/lib/billing'
10+
import { hasWorkspaceAccessControlAccess, isWorkspaceOnEnterprisePlan } from '@/lib/billing'
1111
import {
1212
DEFAULT_PERMISSION_GROUP_CONFIG,
1313
type PermissionGroupConfig,
1414
parsePermissionGroupConfig,
15+
permissionGroupConfigSchema,
1516
} from '@/lib/permission-groups/types'
1617
import { checkWorkspaceAccess, hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils'
1718

1819
const logger = createLogger('WorkspacePermissionGroups')
1920

20-
const configSchema = z.object({
21-
allowedIntegrations: z.array(z.string()).nullable().optional(),
22-
allowedModelProviders: z.array(z.string()).nullable().optional(),
23-
hideTraceSpans: z.boolean().optional(),
24-
hideKnowledgeBaseTab: z.boolean().optional(),
25-
hideTablesTab: z.boolean().optional(),
26-
hideCopilot: z.boolean().optional(),
27-
hideIntegrationsTab: z.boolean().optional(),
28-
hideSecretsTab: z.boolean().optional(),
29-
hideApiKeysTab: z.boolean().optional(),
30-
hideInboxTab: z.boolean().optional(),
31-
hideFilesTab: z.boolean().optional(),
32-
disableMcpTools: z.boolean().optional(),
33-
disableCustomTools: z.boolean().optional(),
34-
disableSkills: z.boolean().optional(),
35-
disableInvitations: z.boolean().optional(),
36-
disablePublicApi: z.boolean().optional(),
37-
hideDeployApi: z.boolean().optional(),
38-
hideDeployMcp: z.boolean().optional(),
39-
hideDeployA2a: z.boolean().optional(),
40-
hideDeployChatbot: z.boolean().optional(),
41-
hideDeployTemplate: z.boolean().optional(),
42-
})
43-
4421
const createSchema = z.object({
4522
name: z.string().trim().min(1).max(100),
4623
description: z.string().max(500).optional(),
47-
config: configSchema.optional(),
24+
config: permissionGroupConfigSchema.optional(),
4825
autoAddNewMembers: z.boolean().optional(),
4926
})
5027

@@ -67,6 +44,11 @@ export async function GET(
6744
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
6845
}
6946

47+
const entitled = await isWorkspaceOnEnterprisePlan(workspaceId)
48+
if (!entitled) {
49+
return NextResponse.json({ error: 'Access Control is an Enterprise feature' }, { status: 403 })
50+
}
51+
7052
const groups = await db
7153
.select({
7254
id: permissionGroup.id,

apps/sim/ee/access-control/components/access-control.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
usePermissionGroups,
4040
useRemovePermissionGroupMember,
4141
useUpdatePermissionGroup,
42+
useUserPermissionConfig,
4243
} from '@/ee/access-control/hooks/permission-groups'
4344
import { useBlacklistedProviders } from '@/hooks/queries/allowed-providers'
4445
import { useWorkspacePermissionsQuery } from '@/hooks/queries/workspace'
@@ -255,6 +256,8 @@ export function AccessControl() {
255256
const { data: workspacePermissionsData, isPending: permsLoading } = useWorkspacePermissionsQuery(
256257
workspaceId ?? null
257258
)
259+
const { data: userPermissionConfig, isPending: entitlementLoading } =
260+
useUserPermissionConfig(workspaceId)
258261

259262
const currentUserIsWorkspaceAdmin = useMemo(() => {
260263
if (!workspacePermissionsData || !session?.user?.id) return false
@@ -264,9 +267,10 @@ export function AccessControl() {
264267
}, [workspacePermissionsData, session?.user?.id])
265268

266269
const accessControlEnabledLocally = isTruthy(getEnv('NEXT_PUBLIC_ACCESS_CONTROL_ENABLED'))
267-
const canManage = accessControlEnabledLocally || currentUserIsWorkspaceAdmin
270+
const isEntitled = accessControlEnabledLocally || !!userPermissionConfig?.entitled
271+
const canManage = isEntitled && currentUserIsWorkspaceAdmin
268272

269-
const isLoading = !workspaceId || groupsLoading || permsLoading
273+
const isLoading = !workspaceId || groupsLoading || permsLoading || entitlementLoading
270274

271275
const createPermissionGroup = useCreatePermissionGroup()
272276
const updatePermissionGroup = useUpdatePermissionGroup()

apps/sim/ee/access-control/hooks/permission-groups.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface UserPermissionConfig {
3131
permissionGroupId: string | null
3232
groupName: string | null
3333
config: PermissionGroupConfig | null
34+
entitled: boolean
3435
}
3536

3637
export const permissionGroupKeys = {

apps/sim/lib/permission-groups/types.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
import { z } from 'zod'
2+
3+
export const permissionGroupConfigSchema = z.object({
4+
allowedIntegrations: z.array(z.string()).nullable().optional(),
5+
allowedModelProviders: z.array(z.string()).nullable().optional(),
6+
hideTraceSpans: z.boolean().optional(),
7+
hideKnowledgeBaseTab: z.boolean().optional(),
8+
hideTablesTab: z.boolean().optional(),
9+
hideCopilot: z.boolean().optional(),
10+
hideIntegrationsTab: z.boolean().optional(),
11+
hideSecretsTab: z.boolean().optional(),
12+
hideApiKeysTab: z.boolean().optional(),
13+
hideInboxTab: z.boolean().optional(),
14+
hideFilesTab: z.boolean().optional(),
15+
disableMcpTools: z.boolean().optional(),
16+
disableCustomTools: z.boolean().optional(),
17+
disableSkills: z.boolean().optional(),
18+
disableInvitations: z.boolean().optional(),
19+
disablePublicApi: z.boolean().optional(),
20+
hideDeployApi: z.boolean().optional(),
21+
hideDeployMcp: z.boolean().optional(),
22+
hideDeployA2a: z.boolean().optional(),
23+
hideDeployChatbot: z.boolean().optional(),
24+
hideDeployTemplate: z.boolean().optional(),
25+
})
26+
127
export interface PermissionGroupConfig {
228
allowedIntegrations: string[] | null
329
allowedModelProviders: string[] | null

0 commit comments

Comments
 (0)