mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
* hide form deployment tab from docs * progress * fix resolution * cleanup code * fix positioning * cleanup dead sockets adv mode ops * address greptile comments * fix tests plus more simplification * fix cleanup * bring back advanced mode with specific definition * revert feature flags * improvement(subblock): ui * resolver change to make all var references optional chaining * fix(webhooks/schedules): deployment version friendly * fix tests * fix credential sets with new lifecycle * prep merge * add back migration * fix display check for adv fields * fix trigger vs block scoping --------- Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
230 lines
5.7 KiB
TypeScript
230 lines
5.7 KiB
TypeScript
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()
|
|
}
|
|
}
|