mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(ee): add enterprise modules (#3121)
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
hydrateUserFilesWithBase64,
|
||||
} from '@/lib/uploads/utils/user-file-base64.server'
|
||||
import { sanitizeInputFormat, sanitizeTools } from '@/lib/workflows/comparison/normalize'
|
||||
import { validateBlockType } from '@/ee/access-control/utils/permission-check'
|
||||
import {
|
||||
BlockType,
|
||||
buildResumeApiUrl,
|
||||
@@ -31,7 +32,6 @@ import { streamingResponseFormatProcessor } from '@/executor/utils'
|
||||
import { buildBlockExecutionError, normalizeError } from '@/executor/utils/errors'
|
||||
import { isJSONString } from '@/executor/utils/json'
|
||||
import { filterOutputForLog } from '@/executor/utils/output-filter'
|
||||
import { validateBlockType } from '@/executor/utils/permission-check'
|
||||
import type { VariableResolver } from '@/executor/variables/resolver'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
import type { SubflowType } from '@/stores/workflows/workflow/types'
|
||||
|
||||
@@ -6,6 +6,12 @@ import { createMcpToolId } from '@/lib/mcp/utils'
|
||||
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
import { getAllBlocks } from '@/blocks'
|
||||
import type { BlockOutput } from '@/blocks/types'
|
||||
import {
|
||||
validateBlockType,
|
||||
validateCustomToolsAllowed,
|
||||
validateMcpToolsAllowed,
|
||||
validateModelProvider,
|
||||
} from '@/ee/access-control/utils/permission-check'
|
||||
import { AGENT, BlockType, DEFAULTS, REFERENCE, stripCustomToolPrefix } from '@/executor/constants'
|
||||
import { memoryService } from '@/executor/handlers/agent/memory'
|
||||
import type {
|
||||
@@ -18,12 +24,6 @@ import type { BlockHandler, ExecutionContext, StreamingExecution } from '@/execu
|
||||
import { collectBlockData } from '@/executor/utils/block-data'
|
||||
import { buildAPIUrl, buildAuthHeaders } from '@/executor/utils/http'
|
||||
import { stringifyJSON } from '@/executor/utils/json'
|
||||
import {
|
||||
validateBlockType,
|
||||
validateCustomToolsAllowed,
|
||||
validateMcpToolsAllowed,
|
||||
validateModelProvider,
|
||||
} from '@/executor/utils/permission-check'
|
||||
import { executeProviderRequest } from '@/providers'
|
||||
import { getProviderFromModel, transformBlockTool } from '@/providers/utils'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
|
||||
@@ -4,11 +4,11 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
import type { BlockOutput } from '@/blocks/types'
|
||||
import { validateModelProvider } from '@/ee/access-control/utils/permission-check'
|
||||
import { BlockType, DEFAULTS, EVALUATOR } from '@/executor/constants'
|
||||
import type { BlockHandler, ExecutionContext } from '@/executor/types'
|
||||
import { buildAPIUrl, buildAuthHeaders, extractAPIErrorMessage } from '@/executor/utils/http'
|
||||
import { isJSONString, parseJSON, stringifyJSON } from '@/executor/utils/json'
|
||||
import { validateModelProvider } from '@/executor/utils/permission-check'
|
||||
import { calculateCost, getProviderFromModel } from '@/providers/utils'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
import { generateRouterPrompt, generateRouterV2Prompt } from '@/blocks/blocks/router'
|
||||
import type { BlockOutput } from '@/blocks/types'
|
||||
import { validateModelProvider } from '@/ee/access-control/utils/permission-check'
|
||||
import {
|
||||
BlockType,
|
||||
DEFAULTS,
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
} from '@/executor/constants'
|
||||
import type { BlockHandler, ExecutionContext } from '@/executor/types'
|
||||
import { buildAuthHeaders } from '@/executor/utils/http'
|
||||
import { validateModelProvider } from '@/executor/utils/permission-check'
|
||||
import { calculateCost, getProviderFromModel } from '@/providers/utils'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
import { db } from '@sim/db'
|
||||
import { member, permissionGroup, permissionGroupMember } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { isOrganizationOnEnterprisePlan } from '@/lib/billing'
|
||||
import { isAccessControlEnabled, isHosted } from '@/lib/core/config/feature-flags'
|
||||
import {
|
||||
type PermissionGroupConfig,
|
||||
parsePermissionGroupConfig,
|
||||
} from '@/lib/permission-groups/types'
|
||||
import type { ExecutionContext } from '@/executor/types'
|
||||
import { getProviderFromModel } from '@/providers/utils'
|
||||
|
||||
const logger = createLogger('PermissionCheck')
|
||||
|
||||
export class ProviderNotAllowedError extends Error {
|
||||
constructor(providerId: string, model: string) {
|
||||
super(
|
||||
`Provider "${providerId}" is not allowed for model "${model}" based on your permission group settings`
|
||||
)
|
||||
this.name = 'ProviderNotAllowedError'
|
||||
}
|
||||
}
|
||||
|
||||
export class IntegrationNotAllowedError extends Error {
|
||||
constructor(blockType: string) {
|
||||
super(`Integration "${blockType}" is not allowed based on your permission group settings`)
|
||||
this.name = 'IntegrationNotAllowedError'
|
||||
}
|
||||
}
|
||||
|
||||
export class McpToolsNotAllowedError extends Error {
|
||||
constructor() {
|
||||
super('MCP tools are not allowed based on your permission group settings')
|
||||
this.name = 'McpToolsNotAllowedError'
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomToolsNotAllowedError extends Error {
|
||||
constructor() {
|
||||
super('Custom tools are not allowed based on your permission group settings')
|
||||
this.name = 'CustomToolsNotAllowedError'
|
||||
}
|
||||
}
|
||||
|
||||
export class InvitationsNotAllowedError extends Error {
|
||||
constructor() {
|
||||
super('Invitations are not allowed based on your permission group settings')
|
||||
this.name = 'InvitationsNotAllowedError'
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserPermissionConfig(
|
||||
userId: string
|
||||
): Promise<PermissionGroupConfig | null> {
|
||||
if (!isHosted && !isAccessControlEnabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [membership] = await db
|
||||
.select({ organizationId: member.organizationId })
|
||||
.from(member)
|
||||
.where(eq(member.userId, userId))
|
||||
.limit(1)
|
||||
|
||||
if (!membership) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isEnterprise = await isOrganizationOnEnterprisePlan(membership.organizationId)
|
||||
if (!isEnterprise) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [groupMembership] = await db
|
||||
.select({ config: permissionGroup.config })
|
||||
.from(permissionGroupMember)
|
||||
.innerJoin(permissionGroup, eq(permissionGroupMember.permissionGroupId, permissionGroup.id))
|
||||
.where(
|
||||
and(
|
||||
eq(permissionGroupMember.userId, userId),
|
||||
eq(permissionGroup.organizationId, membership.organizationId)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (!groupMembership) {
|
||||
return null
|
||||
}
|
||||
|
||||
return parsePermissionGroupConfig(groupMembership.config)
|
||||
}
|
||||
|
||||
export async function getPermissionConfig(
|
||||
userId: string | undefined,
|
||||
ctx?: ExecutionContext
|
||||
): Promise<PermissionGroupConfig | null> {
|
||||
if (!userId) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
if (ctx.permissionConfigLoaded) {
|
||||
return ctx.permissionConfig ?? null
|
||||
}
|
||||
|
||||
const config = await getUserPermissionConfig(userId)
|
||||
ctx.permissionConfig = config
|
||||
ctx.permissionConfigLoaded = true
|
||||
return config
|
||||
}
|
||||
|
||||
return getUserPermissionConfig(userId)
|
||||
}
|
||||
|
||||
export async function validateModelProvider(
|
||||
userId: string | undefined,
|
||||
model: string,
|
||||
ctx?: ExecutionContext
|
||||
): Promise<void> {
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = await getPermissionConfig(userId, ctx)
|
||||
|
||||
if (!config || config.allowedModelProviders === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const providerId = getProviderFromModel(model)
|
||||
|
||||
if (!config.allowedModelProviders.includes(providerId)) {
|
||||
logger.warn('Model provider blocked by permission group', { userId, model, providerId })
|
||||
throw new ProviderNotAllowedError(providerId, model)
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateBlockType(
|
||||
userId: string | undefined,
|
||||
blockType: string,
|
||||
ctx?: ExecutionContext
|
||||
): Promise<void> {
|
||||
if (blockType === 'start_trigger') {
|
||||
return
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = await getPermissionConfig(userId, ctx)
|
||||
|
||||
if (!config || config.allowedIntegrations === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!config.allowedIntegrations.includes(blockType)) {
|
||||
logger.warn('Integration blocked by permission group', { userId, blockType })
|
||||
throw new IntegrationNotAllowedError(blockType)
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateMcpToolsAllowed(
|
||||
userId: string | undefined,
|
||||
ctx?: ExecutionContext
|
||||
): Promise<void> {
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = await getPermissionConfig(userId, ctx)
|
||||
|
||||
if (!config) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config.disableMcpTools) {
|
||||
logger.warn('MCP tools blocked by permission group', { userId })
|
||||
throw new McpToolsNotAllowedError()
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateCustomToolsAllowed(
|
||||
userId: string | undefined,
|
||||
ctx?: ExecutionContext
|
||||
): Promise<void> {
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = await getPermissionConfig(userId, ctx)
|
||||
|
||||
if (!config) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config.disableCustomTools) {
|
||||
logger.warn('Custom tools blocked by permission group', { userId })
|
||||
throw new CustomToolsNotAllowedError()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the user is allowed to send invitations.
|
||||
* Also checks the global feature flag.
|
||||
*/
|
||||
export async function validateInvitationsAllowed(userId: string | undefined): Promise<void> {
|
||||
const { isInvitationsDisabled } = await import('@/lib/core/config/feature-flags')
|
||||
if (isInvitationsDisabled) {
|
||||
logger.warn('Invitations blocked by feature flag')
|
||||
throw new InvitationsNotAllowedError()
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = await getUserPermissionConfig(userId)
|
||||
|
||||
if (!config) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config.disableInvitations) {
|
||||
logger.warn('Invitations blocked by permission group', { userId })
|
||||
throw new InvitationsNotAllowedError()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user