From 9c3fd1f7af7b20779d94fc783e06a58517b9e276 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 2 Feb 2026 23:40:18 -0800 Subject: [PATCH] feat(ee): add enterprise modules (#3121) --- apps/sim/app/(auth)/sso/page.tsx | 2 +- .../organizations/[id]/invitations/route.ts | 2 +- .../api/workspaces/invitations/route.test.ts | 2 +- .../app/api/workspaces/invitations/route.ts | 5 +- apps/sim/app/chat/[identifier]/chat.tsx | 2 +- apps/sim/app/chat/components/index.ts | 1 - .../[workspaceId]/knowledge/page.tsx | 3 +- .../[workspaceId]/templates/page.tsx | 2 +- .../credential-sets/credential-sets.tsx | 12 ----- .../settings-modal/components/index.ts | 2 - .../settings-modal/components/mcp/mcp.tsx | 3 -- .../settings-modal/settings-modal.tsx | 6 +-- apps/sim/ee/LICENSE | 43 +++++++++++++++++ apps/sim/ee/README.md | 21 ++++++++ .../components}/access-control.tsx | 11 ++--- .../hooks}/permission-groups.ts | 2 + .../access-control}/utils/permission-check.ts | 0 .../sso => ee/sso/components}/sso-auth.tsx | 0 .../sso => ee/sso/components}/sso-form.tsx | 0 .../sso/components/sso-settings.tsx} | 48 ++----------------- apps/sim/{lib/auth => ee}/sso/constants.ts | 4 ++ .../{hooks/queries => ee/sso/hooks}/sso.ts | 38 +-------------- apps/sim/executor/execution/block-executor.ts | 2 +- .../executor/handlers/agent/agent-handler.ts | 12 ++--- .../handlers/evaluator/evaluator-handler.ts | 2 +- .../handlers/router/router-handler.ts | 2 +- apps/sim/hooks/queries/credential-sets.ts | 2 + apps/sim/hooks/use-permission-config.ts | 4 +- apps/sim/lib/auth/auth.ts | 2 +- apps/sim/lib/copilot/process-contents.ts | 2 +- .../tools/server/blocks/get-block-config.ts | 2 +- .../tools/server/blocks/get-block-options.ts | 2 +- .../server/blocks/get-blocks-and-tools.ts | 2 +- .../server/blocks/get-blocks-metadata-tool.ts | 2 +- .../tools/server/blocks/get-trigger-blocks.ts | 2 +- .../tools/server/workflow/edit-workflow.ts | 2 +- 36 files changed, 110 insertions(+), 139 deletions(-) create mode 100644 apps/sim/ee/LICENSE create mode 100644 apps/sim/ee/README.md rename apps/sim/{app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/access-control => ee/access-control/components}/access-control.tsx (99%) rename apps/sim/{hooks/queries => ee/access-control/hooks}/permission-groups.ts (99%) rename apps/sim/{executor => ee/access-control}/utils/permission-check.ts (100%) rename apps/sim/{app/chat/components/auth/sso => ee/sso/components}/sso-auth.tsx (100%) rename apps/sim/{app/(auth)/sso => ee/sso/components}/sso-form.tsx (100%) rename apps/sim/{app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/sso/sso.tsx => ee/sso/components/sso-settings.tsx} (97%) rename apps/sim/{lib/auth => ee}/sso/constants.ts (85%) rename apps/sim/{hooks/queries => ee/sso/hooks}/sso.ts (69%) diff --git a/apps/sim/app/(auth)/sso/page.tsx b/apps/sim/app/(auth)/sso/page.tsx index 18ff14f90..49bf30f1c 100644 --- a/apps/sim/app/(auth)/sso/page.tsx +++ b/apps/sim/app/(auth)/sso/page.tsx @@ -1,6 +1,6 @@ import { redirect } from 'next/navigation' import { getEnv, isTruthy } from '@/lib/core/config/env' -import SSOForm from '@/app/(auth)/sso/sso-form' +import SSOForm from '@/ee/sso/components/sso-form' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/organizations/[id]/invitations/route.ts b/apps/sim/app/api/organizations/[id]/invitations/route.ts index 124d70957..905628696 100644 --- a/apps/sim/app/api/organizations/[id]/invitations/route.ts +++ b/apps/sim/app/api/organizations/[id]/invitations/route.ts @@ -29,7 +29,7 @@ import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils' import { InvitationsNotAllowedError, validateInvitationsAllowed, -} from '@/executor/utils/permission-check' +} from '@/ee/access-control/utils/permission-check' const logger = createLogger('OrganizationInvitations') diff --git a/apps/sim/app/api/workspaces/invitations/route.test.ts b/apps/sim/app/api/workspaces/invitations/route.test.ts index 202559142..ac3545885 100644 --- a/apps/sim/app/api/workspaces/invitations/route.test.ts +++ b/apps/sim/app/api/workspaces/invitations/route.test.ts @@ -102,7 +102,7 @@ describe('Workspace Invitations API Route', () => { inArray: vi.fn().mockImplementation((field, values) => ({ type: 'inArray', field, values })), })) - vi.doMock('@/executor/utils/permission-check', () => ({ + vi.doMock('@/ee/access-control/utils/permission-check', () => ({ validateInvitationsAllowed: vi.fn().mockResolvedValue(undefined), InvitationsNotAllowedError: class InvitationsNotAllowedError extends Error { constructor() { diff --git a/apps/sim/app/api/workspaces/invitations/route.ts b/apps/sim/app/api/workspaces/invitations/route.ts index bd70b9dc9..e6116d840 100644 --- a/apps/sim/app/api/workspaces/invitations/route.ts +++ b/apps/sim/app/api/workspaces/invitations/route.ts @@ -21,7 +21,7 @@ import { getFromEmailAddress } from '@/lib/messaging/email/utils' import { InvitationsNotAllowedError, validateInvitationsAllowed, -} from '@/executor/utils/permission-check' +} from '@/ee/access-control/utils/permission-check' export const dynamic = 'force-dynamic' @@ -38,7 +38,6 @@ export async function GET(req: NextRequest) { } try { - // Get all workspaces where the user has permissions const userWorkspaces = await db .select({ id: workspace.id }) .from(workspace) @@ -55,10 +54,8 @@ export async function GET(req: NextRequest) { return NextResponse.json({ invitations: [] }) } - // Get all workspaceIds where the user is a member const workspaceIds = userWorkspaces.map((w) => w.id) - // Find all invitations for those workspaces const invitations = await db .select() .from(workspaceInvitation) diff --git a/apps/sim/app/chat/[identifier]/chat.tsx b/apps/sim/app/chat/[identifier]/chat.tsx index 94082ffec..549e450d4 100644 --- a/apps/sim/app/chat/[identifier]/chat.tsx +++ b/apps/sim/app/chat/[identifier]/chat.tsx @@ -14,11 +14,11 @@ import { ChatMessageContainer, EmailAuth, PasswordAuth, - SSOAuth, VoiceInterface, } from '@/app/chat/components' import { CHAT_ERROR_MESSAGES, CHAT_REQUEST_TIMEOUT_MS } from '@/app/chat/constants' import { useAudioStreaming, useChatStreaming } from '@/app/chat/hooks' +import SSOAuth from '@/ee/sso/components/sso-auth' const logger = createLogger('ChatClient') diff --git a/apps/sim/app/chat/components/index.ts b/apps/sim/app/chat/components/index.ts index 4be7ea2f1..eef5a82c4 100644 --- a/apps/sim/app/chat/components/index.ts +++ b/apps/sim/app/chat/components/index.ts @@ -1,6 +1,5 @@ export { default as EmailAuth } from './auth/email/email-auth' export { default as PasswordAuth } from './auth/password/password-auth' -export { default as SSOAuth } from './auth/sso/sso-auth' export { ChatErrorState } from './error-state/error-state' export { ChatHeader } from './header/header' export { ChatInput } from './input/input' diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/page.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/page.tsx index a5c1eadeb..a449539d5 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/page.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/page.tsx @@ -1,7 +1,7 @@ import { redirect } from 'next/navigation' import { getSession } from '@/lib/auth' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { Knowledge } from './knowledge' interface KnowledgePageProps { @@ -23,7 +23,6 @@ export default async function KnowledgePage({ params }: KnowledgePageProps) { redirect('/') } - // Check permission group restrictions const permissionConfig = await getUserPermissionConfig(session.user.id) if (permissionConfig?.hideKnowledgeBaseTab) { redirect(`/workspace/${workspaceId}`) diff --git a/apps/sim/app/workspace/[workspaceId]/templates/page.tsx b/apps/sim/app/workspace/[workspaceId]/templates/page.tsx index 9955c2433..8e5194cee 100644 --- a/apps/sim/app/workspace/[workspaceId]/templates/page.tsx +++ b/apps/sim/app/workspace/[workspaceId]/templates/page.tsx @@ -6,7 +6,7 @@ import { getSession } from '@/lib/auth' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' import type { Template as WorkspaceTemplate } from '@/app/workspace/[workspaceId]/templates/templates' import Templates from '@/app/workspace/[workspaceId]/templates/templates' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' interface TemplatesPageProps { params: Promise<{ diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credential-sets/credential-sets.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credential-sets/credential-sets.tsx index a1fae5b1a..ce6154939 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credential-sets/credential-sets.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credential-sets/credential-sets.tsx @@ -246,7 +246,6 @@ export function CredentialSets() { setNewSetDescription('') setNewSetProvider('google-email') - // Open detail view for the newly created group if (result?.credentialSet) { setViewingSet(result.credentialSet) } @@ -336,7 +335,6 @@ export function CredentialSets() { email, }) - // Start 60s cooldown setResendCooldowns((prev) => ({ ...prev, [invitationId]: 60 })) const interval = setInterval(() => { setResendCooldowns((prev) => { @@ -393,7 +391,6 @@ export function CredentialSets() { return } - // All hooks must be called before any early returns const activeMemberships = useMemo( () => memberships.filter((m) => m.status === 'active'), [memberships] @@ -447,7 +444,6 @@ export function CredentialSets() {
- {/* Group Info */}
@@ -471,7 +467,6 @@ export function CredentialSets() {
- {/* Invite Section - Email Tags Input */}
{emailError}

}
- {/* Members List - styled like team members */}

Members

@@ -519,7 +513,6 @@ export function CredentialSets() {

) : (
- {/* Active Members */} {activeMembers.map((member) => { const name = member.userName || 'Unknown' const avatarInitial = name.charAt(0).toUpperCase() @@ -572,7 +565,6 @@ export function CredentialSets() { ) })} - {/* Pending Invitations */} {pendingInvitations.map((invitation) => { const email = invitation.email || 'Unknown' const emailPrefix = email.split('@')[0] @@ -641,7 +633,6 @@ export function CredentialSets() {
- {/* Footer Actions */}
- {/* Create Polling Group Modal */} Create Polling Group @@ -895,7 +885,6 @@ export function CredentialSets() { - {/* Leave Confirmation Modal */} setLeavingMembership(null)}> Leave Polling Group @@ -923,7 +912,6 @@ export function CredentialSets() { - {/* Delete Confirmation Modal */} setDeletingSet(null)}> Delete Polling Group diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/index.ts index e2241137f..db87eaf39 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/index.ts @@ -1,4 +1,3 @@ -export { AccessControl } from './access-control/access-control' export { ApiKeys } from './api-keys/api-keys' export { BYOK } from './byok/byok' export { Copilot } from './copilot/copilot' @@ -10,7 +9,6 @@ export { Files as FileUploads } from './files/files' export { General } from './general/general' export { Integrations } from './integrations/integrations' export { MCP } from './mcp/mcp' -export { SSO } from './sso/sso' export { Subscription } from './subscription/subscription' export { TeamManagement } from './team-management/team-management' export { WorkflowMcpServers } from './workflow-mcp-servers/workflow-mcp-servers' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx index d4103702b..89dc83172 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx @@ -407,14 +407,12 @@ export function MCP({ initialServerId }: MCPProps) { const [urlScrollLeft, setUrlScrollLeft] = useState(0) const [headerScrollLeft, setHeaderScrollLeft] = useState>({}) - // Auto-select server when initialServerId is provided useEffect(() => { if (initialServerId && servers.some((s) => s.id === initialServerId)) { setSelectedServerId(initialServerId) } }, [initialServerId, servers]) - // Force refresh tools when entering server detail view to detect stale schemas useEffect(() => { if (selectedServerId) { forceRefreshTools(workspaceId) @@ -717,7 +715,6 @@ export function MCP({ initialServerId }: MCPProps) { `Refreshed MCP server: ${serverId}, workflows updated: ${result.workflowsUpdated}` ) - // If the active workflow was updated, reload its subblock values from DB const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId if (activeWorkflowId && result.updatedWorkflowIds?.includes(activeWorkflowId)) { logger.info(`Active workflow ${activeWorkflowId} was updated, reloading subblock values`) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx index d2a72a998..f0b749f68 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx @@ -41,7 +41,6 @@ import { getEnv, isTruthy } from '@/lib/core/config/env' import { isHosted } from '@/lib/core/config/feature-flags' import { getUserRole } from '@/lib/workspaces/organization' import { - AccessControl, ApiKeys, BYOK, Copilot, @@ -53,15 +52,16 @@ import { General, Integrations, MCP, - SSO, Subscription, TeamManagement, WorkflowMcpServers, } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components' import { TemplateProfile } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/template-profile/template-profile' +import { AccessControl } from '@/ee/access-control/components/access-control' +import { SSO } from '@/ee/sso/components/sso-settings' +import { ssoKeys, useSSOProviders } from '@/ee/sso/hooks/sso' import { generalSettingsKeys, useGeneralSettings } from '@/hooks/queries/general-settings' import { organizationKeys, useOrganizations } from '@/hooks/queries/organization' -import { ssoKeys, useSSOProviders } from '@/hooks/queries/sso' import { subscriptionKeys, useSubscriptionData } from '@/hooks/queries/subscription' import { usePermissionConfig } from '@/hooks/use-permission-config' import { useSettingsModalStore } from '@/stores/modals/settings/store' diff --git a/apps/sim/ee/LICENSE b/apps/sim/ee/LICENSE new file mode 100644 index 000000000..ba5405dbf --- /dev/null +++ b/apps/sim/ee/LICENSE @@ -0,0 +1,43 @@ +Sim Enterprise License + +Copyright (c) 2025-present Sim Studio, Inc. + +This software and associated documentation files (the "Software") are licensed +under the following terms: + +1. LICENSE GRANT + + Subject to the terms of this license, Sim Studio, Inc. grants you a limited, + non-exclusive, non-transferable license to use the Software for: + + - Development, testing, and evaluation purposes + - Internal non-production use + + Production use of the Software requires a valid Sim Enterprise subscription. + +2. RESTRICTIONS + + You may not: + + - Use the Software in production without a valid Enterprise subscription + - Modify, adapt, or create derivative works of the Software + - Redistribute, sublicense, or transfer the Software + - Remove or alter any proprietary notices in the Software + +3. ENTERPRISE SUBSCRIPTION + + Production deployment of enterprise features requires an active Sim Enterprise + subscription. Contact sales@simstudio.ai for licensing information. + +4. DISCLAIMER + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + +5. LIMITATION OF LIABILITY + + IN NO EVENT SHALL SIM STUDIO, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY ARISING FROM THE USE OF THE SOFTWARE. + +For questions about enterprise licensing, contact: sales@simstudio.ai diff --git a/apps/sim/ee/README.md b/apps/sim/ee/README.md new file mode 100644 index 000000000..d9e91afaf --- /dev/null +++ b/apps/sim/ee/README.md @@ -0,0 +1,21 @@ +# Sim Enterprise Edition + +This directory contains enterprise features that require a Sim Enterprise subscription +for production use. + +## Features + +- **SSO (Single Sign-On)**: OIDC and SAML authentication integration +- **Access Control**: Permission groups for fine-grained user access management +- **Credential Sets**: Shared credential pools for email polling workflows + +## Licensing + +See [LICENSE](./LICENSE) for terms. Development and testing use is permitted. +Production deployment requires an active Enterprise subscription. + +## Architecture + +Enterprise features are imported directly throughout the codebase. The `ee/` directory +is required at build time. Feature visibility is controlled at runtime via environment +variables (e.g., `NEXT_PUBLIC_ACCESS_CONTROL_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED`). diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/access-control/access-control.tsx b/apps/sim/ee/access-control/components/access-control.tsx similarity index 99% rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/access-control/access-control.tsx rename to apps/sim/ee/access-control/components/access-control.tsx index af7db3fcc..83f2f28dc 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/access-control/access-control.tsx +++ b/apps/sim/ee/access-control/components/access-control.tsx @@ -29,7 +29,6 @@ import type { PermissionGroupConfig } from '@/lib/permission-groups/types' import { getUserColor } from '@/lib/workspaces/colors' import { getUserRole } from '@/lib/workspaces/organization' import { getAllBlocks } from '@/blocks' -import { useOrganization, useOrganizations } from '@/hooks/queries/organization' import { type PermissionGroup, useBulkAddPermissionGroupMembers, @@ -39,7 +38,8 @@ import { usePermissionGroups, useRemovePermissionGroupMember, useUpdatePermissionGroup, -} from '@/hooks/queries/permission-groups' +} from '@/ee/access-control/hooks/permission-groups' +import { useOrganization, useOrganizations } from '@/hooks/queries/organization' import { useSubscriptionData } from '@/hooks/queries/subscription' import { PROVIDER_DEFINITIONS } from '@/providers/models' import { getAllProviderIds } from '@/providers/utils' @@ -255,7 +255,6 @@ export function AccessControl() { queryEnabled ) - // Show loading while dependencies load, or while permission groups query is pending const isLoading = orgsLoading || subLoading || (queryEnabled && groupsLoading) const { data: organization } = useOrganization(activeOrganization?.id || '') @@ -410,10 +409,8 @@ export function AccessControl() { }, [viewingGroup, editingConfig]) const allBlocks = useMemo(() => { - // Filter out hidden blocks and start_trigger (which should never be disabled) const blocks = getAllBlocks().filter((b) => !b.hideFromToolbar && b.type !== 'start_trigger') return blocks.sort((a, b) => { - // Group by category: triggers first, then blocks, then tools const categoryOrder = { triggers: 0, blocks: 1, tools: 2 } const catA = categoryOrder[a.category] ?? 3 const catB = categoryOrder[b.category] ?? 3 @@ -555,10 +552,9 @@ export function AccessControl() { }, [viewingGroup, editingConfig, activeOrganization?.id, updatePermissionGroup]) const handleOpenAddMembersModal = useCallback(() => { - const existingMemberUserIds = new Set(members.map((m) => m.userId)) setSelectedMemberIds(new Set()) setShowAddMembersModal(true) - }, [members]) + }, []) const handleAddSelectedMembers = useCallback(async () => { if (!viewingGroup || selectedMemberIds.size === 0) return @@ -891,7 +887,6 @@ export function AccessControl() { prev ? { ...prev, - // When deselecting all, keep start_trigger allowed (it should never be disabled) allowedIntegrations: allAllowed ? ['start_trigger'] : null, } : prev diff --git a/apps/sim/hooks/queries/permission-groups.ts b/apps/sim/ee/access-control/hooks/permission-groups.ts similarity index 99% rename from apps/sim/hooks/queries/permission-groups.ts rename to apps/sim/ee/access-control/hooks/permission-groups.ts index 6832d5188..91f838ced 100644 --- a/apps/sim/hooks/queries/permission-groups.ts +++ b/apps/sim/ee/access-control/hooks/permission-groups.ts @@ -1,3 +1,5 @@ +'use client' + import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import type { PermissionGroupConfig } from '@/lib/permission-groups/types' import { fetchJson } from '@/hooks/selectors/helpers' diff --git a/apps/sim/executor/utils/permission-check.ts b/apps/sim/ee/access-control/utils/permission-check.ts similarity index 100% rename from apps/sim/executor/utils/permission-check.ts rename to apps/sim/ee/access-control/utils/permission-check.ts diff --git a/apps/sim/app/chat/components/auth/sso/sso-auth.tsx b/apps/sim/ee/sso/components/sso-auth.tsx similarity index 100% rename from apps/sim/app/chat/components/auth/sso/sso-auth.tsx rename to apps/sim/ee/sso/components/sso-auth.tsx diff --git a/apps/sim/app/(auth)/sso/sso-form.tsx b/apps/sim/ee/sso/components/sso-form.tsx similarity index 100% rename from apps/sim/app/(auth)/sso/sso-form.tsx rename to apps/sim/ee/sso/components/sso-form.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/sso/sso.tsx b/apps/sim/ee/sso/components/sso-settings.tsx similarity index 97% rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/sso/sso.tsx rename to apps/sim/ee/sso/components/sso-settings.tsx index 2657c8204..a43e15ff3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/sso/sso.tsx +++ b/apps/sim/ee/sso/components/sso-settings.tsx @@ -11,55 +11,13 @@ import { isBillingEnabled } from '@/lib/core/config/feature-flags' import { cn } from '@/lib/core/utils/cn' import { getBaseUrl } from '@/lib/core/utils/urls' import { getUserRole } from '@/lib/workspaces/organization/utils' +import { SSO_TRUSTED_PROVIDERS } from '@/ee/sso/constants' +import { useConfigureSSO, useSSOProviders } from '@/ee/sso/hooks/sso' import { useOrganizations } from '@/hooks/queries/organization' -import { useConfigureSSO, useSSOProviders } from '@/hooks/queries/sso' import { useSubscriptionData } from '@/hooks/queries/subscription' const logger = createLogger('SSO') -const TRUSTED_SSO_PROVIDERS = [ - 'okta', - 'okta-saml', - 'okta-prod', - 'okta-dev', - 'okta-staging', - 'okta-test', - 'azure-ad', - 'azure-active-directory', - 'azure-corp', - 'azure-enterprise', - 'adfs', - 'adfs-company', - 'adfs-corp', - 'adfs-enterprise', - 'auth0', - 'auth0-prod', - 'auth0-dev', - 'auth0-staging', - 'onelogin', - 'onelogin-prod', - 'onelogin-corp', - 'jumpcloud', - 'jumpcloud-prod', - 'jumpcloud-corp', - 'ping-identity', - 'ping-federate', - 'pingone', - 'shibboleth', - 'shibboleth-idp', - 'google-workspace', - 'google-sso', - 'saml', - 'saml2', - 'saml-sso', - 'oidc', - 'oidc-sso', - 'openid-connect', - 'custom-sso', - 'enterprise-sso', - 'company-sso', -] - interface SSOProvider { id: string providerId: string @@ -565,7 +523,7 @@ export function SSO() { handleInputChange('providerId', value)} - options={TRUSTED_SSO_PROVIDERS.map((id) => ({ + options={SSO_TRUSTED_PROVIDERS.map((id) => ({ label: id, value: id, }))} diff --git a/apps/sim/lib/auth/sso/constants.ts b/apps/sim/ee/sso/constants.ts similarity index 85% rename from apps/sim/lib/auth/sso/constants.ts rename to apps/sim/ee/sso/constants.ts index ca246f8cf..67cfee94f 100644 --- a/apps/sim/lib/auth/sso/constants.ts +++ b/apps/sim/ee/sso/constants.ts @@ -1,3 +1,7 @@ +/** + * List of trusted SSO provider identifiers. + * Used for validation and autocomplete in SSO configuration. + */ export const SSO_TRUSTED_PROVIDERS = [ 'okta', 'okta-saml', diff --git a/apps/sim/hooks/queries/sso.ts b/apps/sim/ee/sso/hooks/sso.ts similarity index 69% rename from apps/sim/hooks/queries/sso.ts rename to apps/sim/ee/sso/hooks/sso.ts index 7c5c769ab..2dfa1592e 100644 --- a/apps/sim/hooks/queries/sso.ts +++ b/apps/sim/ee/sso/hooks/sso.ts @@ -1,3 +1,5 @@ +'use client' + import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { organizationKeys } from '@/hooks/queries/organization' @@ -75,39 +77,3 @@ export function useConfigureSSO() { }, }) } - -/** - * Delete SSO provider mutation - */ -interface DeleteSSOParams { - providerId: string - orgId?: string -} - -export function useDeleteSSO() { - const queryClient = useQueryClient() - - return useMutation({ - mutationFn: async ({ providerId }: DeleteSSOParams) => { - const response = await fetch(`/api/auth/sso/providers/${providerId}`, { - method: 'DELETE', - }) - - if (!response.ok) { - const error = await response.json() - throw new Error(error.message || 'Failed to delete SSO provider') - } - - return response.json() - }, - onSuccess: (_data, variables) => { - queryClient.invalidateQueries({ queryKey: ssoKeys.providers() }) - - if (variables.orgId) { - queryClient.invalidateQueries({ - queryKey: organizationKeys.detail(variables.orgId), - }) - } - }, - }) -} diff --git a/apps/sim/executor/execution/block-executor.ts b/apps/sim/executor/execution/block-executor.ts index d17da0e7c..59b08e4a9 100644 --- a/apps/sim/executor/execution/block-executor.ts +++ b/apps/sim/executor/execution/block-executor.ts @@ -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' diff --git a/apps/sim/executor/handlers/agent/agent-handler.ts b/apps/sim/executor/handlers/agent/agent-handler.ts index 007833d9c..40c7b9ba8 100644 --- a/apps/sim/executor/handlers/agent/agent-handler.ts +++ b/apps/sim/executor/handlers/agent/agent-handler.ts @@ -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' diff --git a/apps/sim/executor/handlers/evaluator/evaluator-handler.ts b/apps/sim/executor/handlers/evaluator/evaluator-handler.ts index b383bdce0..3e95b2f85 100644 --- a/apps/sim/executor/handlers/evaluator/evaluator-handler.ts +++ b/apps/sim/executor/handlers/evaluator/evaluator-handler.ts @@ -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' diff --git a/apps/sim/executor/handlers/router/router-handler.ts b/apps/sim/executor/handlers/router/router-handler.ts index 12acf6c4c..766a4aac6 100644 --- a/apps/sim/executor/handlers/router/router-handler.ts +++ b/apps/sim/executor/handlers/router/router-handler.ts @@ -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' diff --git a/apps/sim/hooks/queries/credential-sets.ts b/apps/sim/hooks/queries/credential-sets.ts index 33da082f7..a18374b75 100644 --- a/apps/sim/hooks/queries/credential-sets.ts +++ b/apps/sim/hooks/queries/credential-sets.ts @@ -1,3 +1,5 @@ +'use client' + import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { fetchJson } from '@/hooks/selectors/helpers' diff --git a/apps/sim/hooks/use-permission-config.ts b/apps/sim/hooks/use-permission-config.ts index 994656fdc..3c536caf5 100644 --- a/apps/sim/hooks/use-permission-config.ts +++ b/apps/sim/hooks/use-permission-config.ts @@ -1,3 +1,5 @@ +'use client' + import { useMemo } from 'react' import { getEnv, isTruthy } from '@/lib/core/config/env' import { isAccessControlEnabled, isHosted } from '@/lib/core/config/feature-flags' @@ -5,8 +7,8 @@ import { DEFAULT_PERMISSION_GROUP_CONFIG, type PermissionGroupConfig, } from '@/lib/permission-groups/types' +import { useUserPermissionConfig } from '@/ee/access-control/hooks/permission-groups' import { useOrganizations } from '@/hooks/queries/organization' -import { useUserPermissionConfig } from '@/hooks/queries/permission-groups' export interface PermissionConfigResult { config: PermissionGroupConfig diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 9241eaf09..d5ac1a8c2 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -59,8 +59,8 @@ import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress, getPersonalEmailFrom } from '@/lib/messaging/email/utils' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server' +import { SSO_TRUSTED_PROVIDERS } from '@/ee/sso/constants' import { createAnonymousSession, ensureAnonymousUserExists } from './anonymous' -import { SSO_TRUSTED_PROVIDERS } from './sso/constants' const logger = createLogger('Auth') diff --git a/apps/sim/lib/copilot/process-contents.ts b/apps/sim/lib/copilot/process-contents.ts index ff1dbf497..13a0015f0 100644 --- a/apps/sim/lib/copilot/process-contents.ts +++ b/apps/sim/lib/copilot/process-contents.ts @@ -5,8 +5,8 @@ import { and, eq, isNull } from 'drizzle-orm' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { sanitizeForCopilot } from '@/lib/workflows/sanitization/json-sanitizer' import { isHiddenFromDisplay } from '@/blocks/types' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { escapeRegExp } from '@/executor/constants' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' import type { ChatContext } from '@/stores/panel/copilot/types' export type AgentContextType = diff --git a/apps/sim/lib/copilot/tools/server/blocks/get-block-config.ts b/apps/sim/lib/copilot/tools/server/blocks/get-block-config.ts index 3d6ebba17..cd95577d7 100644 --- a/apps/sim/lib/copilot/tools/server/blocks/get-block-config.ts +++ b/apps/sim/lib/copilot/tools/server/blocks/get-block-config.ts @@ -7,7 +7,7 @@ import { } from '@/lib/copilot/tools/shared/schemas' import { registry as blockRegistry, getLatestBlock } from '@/blocks/registry' import { isHiddenFromDisplay, type SubBlockConfig } from '@/blocks/types' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { PROVIDER_DEFINITIONS } from '@/providers/models' import { tools as toolsRegistry } from '@/tools/registry' import { getTrigger, isTriggerValid } from '@/triggers' diff --git a/apps/sim/lib/copilot/tools/server/blocks/get-block-options.ts b/apps/sim/lib/copilot/tools/server/blocks/get-block-options.ts index b5e5b2373..177482fc3 100644 --- a/apps/sim/lib/copilot/tools/server/blocks/get-block-options.ts +++ b/apps/sim/lib/copilot/tools/server/blocks/get-block-options.ts @@ -6,7 +6,7 @@ import { type GetBlockOptionsResultType, } from '@/lib/copilot/tools/shared/schemas' import { registry as blockRegistry, getLatestBlock } from '@/blocks/registry' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { tools as toolsRegistry } from '@/tools/registry' export const getBlockOptionsServerTool: BaseServerTool< diff --git a/apps/sim/lib/copilot/tools/server/blocks/get-blocks-and-tools.ts b/apps/sim/lib/copilot/tools/server/blocks/get-blocks-and-tools.ts index 222288aab..9413dc278 100644 --- a/apps/sim/lib/copilot/tools/server/blocks/get-blocks-and-tools.ts +++ b/apps/sim/lib/copilot/tools/server/blocks/get-blocks-and-tools.ts @@ -6,7 +6,7 @@ import { } from '@/lib/copilot/tools/shared/schemas' import { registry as blockRegistry } from '@/blocks/registry' import type { BlockConfig } from '@/blocks/types' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' export const getBlocksAndToolsServerTool: BaseServerTool< ReturnType, diff --git a/apps/sim/lib/copilot/tools/server/blocks/get-blocks-metadata-tool.ts b/apps/sim/lib/copilot/tools/server/blocks/get-blocks-metadata-tool.ts index dc4615777..7b945d6b0 100644 --- a/apps/sim/lib/copilot/tools/server/blocks/get-blocks-metadata-tool.ts +++ b/apps/sim/lib/copilot/tools/server/blocks/get-blocks-metadata-tool.ts @@ -8,7 +8,7 @@ import { } from '@/lib/copilot/tools/shared/schemas' import { registry as blockRegistry } from '@/blocks/registry' import { AuthMode, type BlockConfig, isHiddenFromDisplay } from '@/blocks/types' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { PROVIDER_DEFINITIONS } from '@/providers/models' import { tools as toolsRegistry } from '@/tools/registry' import { getTrigger, isTriggerValid } from '@/triggers' diff --git a/apps/sim/lib/copilot/tools/server/blocks/get-trigger-blocks.ts b/apps/sim/lib/copilot/tools/server/blocks/get-trigger-blocks.ts index c5f3b75b4..5f5820e20 100644 --- a/apps/sim/lib/copilot/tools/server/blocks/get-trigger-blocks.ts +++ b/apps/sim/lib/copilot/tools/server/blocks/get-trigger-blocks.ts @@ -3,7 +3,7 @@ import { z } from 'zod' import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool' import { registry as blockRegistry } from '@/blocks/registry' import type { BlockConfig } from '@/blocks/types' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' export const GetTriggerBlocksInput = z.object({}) export const GetTriggerBlocksResult = z.object({ diff --git a/apps/sim/lib/copilot/tools/server/workflow/edit-workflow.ts b/apps/sim/lib/copilot/tools/server/workflow/edit-workflow.ts index 61866dbd9..f484ea5d8 100644 --- a/apps/sim/lib/copilot/tools/server/workflow/edit-workflow.ts +++ b/apps/sim/lib/copilot/tools/server/workflow/edit-workflow.ts @@ -15,8 +15,8 @@ import { buildCanonicalIndex, isCanonicalPair } from '@/lib/workflows/subblocks/ import { TriggerUtils } from '@/lib/workflows/triggers/triggers' import { getAllBlocks, getBlock } from '@/blocks/registry' import type { BlockConfig, SubBlockConfig } from '@/blocks/types' +import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check' import { EDGE, normalizeName, RESERVED_BLOCK_NAMES } from '@/executor/constants' -import { getUserPermissionConfig } from '@/executor/utils/permission-check' import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' import { TRIGGER_RUNTIME_SUBBLOCK_IDS } from '@/triggers/constants'