diff --git a/packages/twenty-server/src/engine/api/mcp/mcp.module.ts b/packages/twenty-server/src/engine/api/mcp/mcp.module.ts index 47cdf0efa74..5cfa611d9c5 100644 --- a/packages/twenty-server/src/engine/api/mcp/mcp.module.ts +++ b/packages/twenty-server/src/engine/api/mcp/mcp.module.ts @@ -1,22 +1,21 @@ import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { McpCoreController } from 'src/engine/api/mcp/controllers/mcp-core.controller'; import { McpMetadataController } from 'src/engine/api/mcp/controllers/mcp-metadata.controller'; import { MCPMetadataService } from 'src/engine/api/mcp/services/mcp-metadata.service'; import { McpProtocolService } from 'src/engine/api/mcp/services/mcp-protocol.service'; import { McpToolExecutorService } from 'src/engine/api/mcp/services/mcp-tool-executor.service'; +import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module'; import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { ToolProviderModule } from 'src/engine/core-modules/tool-provider/tool-provider.module'; -import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; @Module({ imports: [ - TypeOrmModule.forFeature([RoleEntity]), + ApiKeyModule, TokenModule, WorkspaceCacheStorageModule, FeatureFlagModule, diff --git a/packages/twenty-server/src/engine/api/mcp/services/__tests__/mcp-protocol.service.spec.ts b/packages/twenty-server/src/engine/api/mcp/services/__tests__/mcp-protocol.service.spec.ts index 73f5400b939..e6d05a67470 100644 --- a/packages/twenty-server/src/engine/api/mcp/services/__tests__/mcp-protocol.service.spec.ts +++ b/packages/twenty-server/src/engine/api/mcp/services/__tests__/mcp-protocol.service.spec.ts @@ -1,6 +1,5 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { Test, type TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; import { jsonSchema } from 'ai'; @@ -9,12 +8,11 @@ import { type JsonRpc } from 'src/engine/api/mcp/dtos/json-rpc'; import { McpProtocolService } from 'src/engine/api/mcp/services/mcp-protocol.service'; import { McpToolExecutorService } from 'src/engine/api/mcp/services/mcp-tool-executor.service'; import { type ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity'; +import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { ToolProviderService } from 'src/engine/core-modules/tool-provider/services/tool-provider.service'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants'; -import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; describe('McpProtocolService', () => { @@ -23,6 +21,7 @@ describe('McpProtocolService', () => { let toolProviderService: jest.Mocked; let userRoleService: jest.Mocked; let mcpToolExecutorService: jest.Mocked; + let apiKeyRoleService: jest.Mocked; const mockWorkspace = { id: 'workspace-1' } as WorkspaceEntity; const mockUserWorkspaceId = 'user-workspace-1'; @@ -51,10 +50,9 @@ describe('McpProtocolService', () => { handleToolsListing: jest.fn(), }; - const mockAdminRole = { - id: mockAdminRoleId, - label: ADMIN_ROLE_LABEL, - } as RoleEntity; + const mockApiKeyRoleService = { + getRoleIdForApiKeyId: jest.fn().mockResolvedValue(mockAdminRoleId), + }; const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -76,10 +74,8 @@ describe('McpProtocolService', () => { useValue: mockMcpToolExecutorService, }, { - provide: getRepositoryToken(RoleEntity), - useValue: { - find: jest.fn().mockResolvedValue([mockAdminRole]), - }, + provide: ApiKeyRoleService, + useValue: mockApiKeyRoleService, }, ], }).compile(); @@ -89,6 +85,7 @@ describe('McpProtocolService', () => { toolProviderService = module.get(ToolProviderService); userRoleService = module.get(UserRoleService); mcpToolExecutorService = module.get(McpToolExecutorService); + apiKeyRoleService = module.get(ApiKeyRoleService); }); it('should be defined', () => { @@ -171,7 +168,7 @@ describe('McpProtocolService', () => { ); }); - it('should return admin role ID when apiKey is provided', async () => { + it('should return role ID from ApiKeyRoleService when apiKey is provided', async () => { const result = await service.getRoleId( 'workspace-1', undefined, @@ -179,6 +176,10 @@ describe('McpProtocolService', () => { ); expect(result).toBe(mockAdminRoleId); + expect(apiKeyRoleService.getRoleIdForApiKeyId).toHaveBeenCalledWith( + mockApiKey.id, + 'workspace-1', + ); }); }); diff --git a/packages/twenty-server/src/engine/api/mcp/services/mcp-protocol.service.ts b/packages/twenty-server/src/engine/api/mcp/services/mcp-protocol.service.ts index 7a655b6021f..8332995c7c2 100644 --- a/packages/twenty-server/src/engine/api/mcp/services/mcp-protocol.service.ts +++ b/packages/twenty-server/src/engine/api/mcp/services/mcp-protocol.service.ts @@ -1,22 +1,21 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; import { isDefined } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; + +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; import { type JsonRpc } from 'src/engine/api/mcp/dtos/json-rpc'; import { McpToolExecutorService } from 'src/engine/api/mcp/services/mcp-tool-executor.service'; import { wrapJsonRpcResponse } from 'src/engine/api/mcp/utils/wrap-jsonrpc-response.util'; -import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity'; +import { type ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity'; +import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum'; import { ToolProviderService } from 'src/engine/core-modules/tool-provider/services/tool-provider.service'; import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; -import { ADMIN_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/admin-role'; @Injectable() export class McpProtocolService { @@ -25,8 +24,7 @@ export class McpProtocolService { private readonly toolProvider: ToolProviderService, private readonly userRoleService: UserRoleService, private readonly mcpToolExecutorService: McpToolExecutorService, - @InjectRepository(RoleEntity) - private readonly roleRepository: Repository, + private readonly apiKeyRoleService: ApiKeyRoleService, ) {} async checkAiEnabled(workspaceId: string): Promise { @@ -64,18 +62,10 @@ export class McpProtocolService { apiKey?: ApiKeyEntity, ) { if (isDefined(apiKey)) { - const [role] = await this.roleRepository.find({ - where: { - workspaceId, - standardId: ADMIN_ROLE.standardId, - }, - }); - - if (!isDefined(role)) { - throw new HttpException('Admin role not found', HttpStatus.FORBIDDEN); - } - - return role.id; + return this.apiKeyRoleService.getRoleIdForApiKeyId( + apiKey.id, + workspaceId, + ); } if (!userWorkspaceId) { @@ -97,6 +87,21 @@ export class McpProtocolService { return roleId; } + private buildAuthContext( + workspace: WorkspaceEntity, + userWorkspaceId?: string, + apiKey?: ApiKeyEntity, + ): WorkspaceAuthContext { + return { + user: null, + apiKey: apiKey ?? null, + application: null, + workspace, + workspaceMemberId: undefined, + userWorkspaceId: userWorkspaceId ?? undefined, + } as WorkspaceAuthContext; + } + async handleMCPCoreQuery( { id, method, params }: JsonRpc, { @@ -132,10 +137,17 @@ export class McpProtocolService { apiKey, ); + const authContext = this.buildAuthContext( + workspace, + userWorkspaceId, + apiKey, + ); + const toolSet = await this.toolProvider.getTools({ workspaceId: workspace.id, categories: [ToolCategory.DATABASE_CRUD, ToolCategory.ACTION], rolePermissionConfig: { unionOf: [roleId] }, + authContext, wrapWithErrorContext: false, // Exclude code_interpreter from MCP to prevent recursive execution attacks // (code running in the sandbox could call code_interpreter via MCP) diff --git a/packages/twenty-server/src/engine/core-modules/application/application.service.ts b/packages/twenty-server/src/engine/core-modules/application/application.service.ts index dabd0524a2d..76f26531ab5 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { isDefined } from 'twenty-shared/utils'; -import { type QueryRunner, Repository } from 'typeorm'; +import { type QueryRunner, type Repository } from 'typeorm'; import { v4 } from 'uuid'; import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; @@ -146,6 +146,32 @@ export class ApplicationService { }); } + async findTwentyStandardApplicationOrThrow(workspaceId: string): Promise<{ + application: ApplicationEntity; + workspace: WorkspaceEntity; + }> { + const workspace = await this.workspaceRepository.findOne({ + where: { id: workspaceId }, + }); + + if (!isDefined(workspace)) { + throw new ApplicationException( + `Could not find workspace ${workspaceId}`, + ApplicationExceptionCode.APPLICATION_NOT_FOUND, + ); + } + + const { twentyStandardFlatApplication } = + await this.findWorkspaceTwentyStandardAndCustomApplicationOrThrow({ + workspace, + }); + + return { + application: twentyStandardFlatApplication as ApplicationEntity, + workspace, + }; + } + async createTwentyStandardApplication( { workspaceId, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts index 8f217806ef3..7e698806fa6 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts @@ -17,7 +17,6 @@ import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output. import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @Injectable() export class CreateRecordService { @@ -30,18 +29,10 @@ export class CreateRecordService { ) {} async execute(params: CreateRecordParams): Promise { - const { objectName, objectRecord, workspaceId, rolePermissionConfig } = + const { objectName, objectRecord, authContext, rolePermissionConfig } = params; - if (!workspaceId) { - return { - success: false, - message: 'Failed to create record: Workspace ID is required', - error: 'Workspace ID not found', - }; - } - - const authContext = buildSystemAuthContext(workspaceId); + const workspaceId = authContext.workspace.id; try { return await this.globalWorkspaceOrmManager.executeInWorkspaceContext( diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts index ccbd8e62844..4669255faba 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts @@ -11,7 +11,6 @@ import { type DeleteRecordParams } from 'src/engine/core-modules/record-crud/typ import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @Injectable() export class DeleteRecordService { @@ -25,18 +24,12 @@ export class DeleteRecordService { const { objectName, objectRecordId, - workspaceId, + authContext, rolePermissionConfig, soft = true, } = params; - if (!workspaceId) { - return { - success: false, - message: 'Failed to delete record: Workspace ID is required', - error: 'Workspace ID not found', - }; - } + const workspaceId = authContext.workspace.id; if (!isDefined(objectRecordId) || !isValidUuid(objectRecordId)) { return { @@ -46,8 +39,6 @@ export class DeleteRecordService { }; } - const authContext = buildSystemAuthContext(workspaceId); - try { return await this.globalWorkspaceOrmManager.executeInWorkspaceContext( authContext, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts index c85777a2cfe..c352e94b6cc 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts @@ -18,7 +18,7 @@ import { RecordCrudExceptionCode, } from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { type FindRecordsParams } from 'src/engine/core-modules/record-crud/types/find-records-params.type'; -import { FindRecordsResult } from 'src/engine/core-modules/record-crud/types/find-records-result.type'; +import { type FindRecordsResult } from 'src/engine/core-modules/record-crud/types/find-records-result.type'; import { getRecordDisplayName } from 'src/engine/core-modules/record-crud/utils/get-record-display-name.util'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; @@ -28,7 +28,6 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @Injectable() export class FindRecordsService { @@ -47,19 +46,11 @@ export class FindRecordsService { orderBy, limit, offset = 0, - workspaceId, + authContext, rolePermissionConfig, } = params; - if (!workspaceId) { - return { - success: false, - message: 'Failed to find records: Workspace ID is required', - error: 'Workspace ID not found', - }; - } - - const authContext = buildSystemAuthContext(workspaceId); + const workspaceId = authContext.workspace.id; try { return await this.globalWorkspaceOrmManager.executeInWorkspaceContext( diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts index 747a9172f1f..0ea1b4987c7 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts @@ -15,7 +15,6 @@ import { RecordInputTransformerService } from 'src/engine/core-modules/record-tr import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @Injectable() export class UpdateRecordService { @@ -32,18 +31,12 @@ export class UpdateRecordService { objectRecordId, objectRecord, fieldsToUpdate, - workspaceId, + authContext, rolePermissionConfig, // updatedBy, } = params; - if (!workspaceId) { - return { - success: false, - message: 'Failed to update record: Workspace ID is required', - error: 'Workspace ID not found', - }; - } + const workspaceId = authContext.workspace.id; if (!isDefined(objectRecordId) || !isValidUuid(objectRecordId)) { return { @@ -53,8 +46,6 @@ export class UpdateRecordService { }; } - const authContext = buildSystemAuthContext(workspaceId); - try { return await this.globalWorkspaceOrmManager.executeInWorkspaceContext( authContext, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts index 035a134eb25..792704c0b99 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts @@ -7,7 +7,7 @@ import { RecordCrudException, RecordCrudExceptionCode, } from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; -import { UpsertRecordParams } from 'src/engine/core-modules/record-crud/types/upsert-record-params.type'; +import { type UpsertRecordParams } from 'src/engine/core-modules/record-crud/types/upsert-record-params.type'; import { getSelectedColumnsFromRestrictedFields } from 'src/engine/core-modules/record-crud/utils/get-selected-columns-from-restricted-fields.util'; import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; @@ -17,7 +17,6 @@ import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field- import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; @Injectable() export class UpsertRecordService { @@ -29,18 +28,10 @@ export class UpsertRecordService { ) {} async execute(params: UpsertRecordParams): Promise { - const { objectName, objectRecord, workspaceId, rolePermissionConfig } = + const { objectName, objectRecord, authContext, rolePermissionConfig } = params; - if (!workspaceId) { - return { - success: false, - message: 'Failed to upsert record: Workspace ID is required', - error: 'Workspace ID not found', - }; - } - - const authContext = buildSystemAuthContext(workspaceId); + const workspaceId = authContext.workspace.id; try { return await this.globalWorkspaceOrmManager.executeInWorkspaceContext( diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/tool-factory/direct-record-tools.factory.ts b/packages/twenty-server/src/engine/core-modules/record-crud/tool-factory/direct-record-tools.factory.ts index dacfab0c0fb..11b8a037db2 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/tool-factory/direct-record-tools.factory.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/tool-factory/direct-record-tools.factory.ts @@ -36,6 +36,14 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { ): ToolSet => { const tools: ToolSet = {}; + // Skip generating tools if no auth context is provided + if (!context.authContext) { + return tools; + } + + // Capture authContext in a constant for use in async callbacks + const authContext = context.authContext; + if (canRead) { tools[`find_${objectMetadata.namePlural}`] = { description: `Search for ${objectMetadata.labelPlural} records using flexible filtering criteria. Supports exact matches, pattern matching, ranges, and null checks. Use limit/offset for pagination and orderBy for sorting. To find by ID, use filter: { id: { eq: "record-id" } }. Returns an array of matching records with their full data.`, @@ -52,7 +60,7 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { orderBy, limit, offset, - workspaceId: context.workspaceId, + authContext, rolePermissionConfig: context.rolePermissionConfig, }); }, @@ -66,7 +74,7 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { objectName: objectMetadata.nameSingular, filter: { id: { eq: parameters.input.id } }, limit: 1, - workspaceId: context.workspaceId, + authContext, rolePermissionConfig: context.rolePermissionConfig, }); }, @@ -84,7 +92,7 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { return deps.createRecordService.execute({ objectName: objectMetadata.nameSingular, objectRecord: parameters.input, - workspaceId: context.workspaceId, + authContext, rolePermissionConfig: context.rolePermissionConfig, createdBy: context.actorContext, }); @@ -112,7 +120,7 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { objectName: objectMetadata.nameSingular, objectRecordId: id, objectRecord, - workspaceId: context.workspaceId, + authContext, rolePermissionConfig: context.rolePermissionConfig, }); }, @@ -127,7 +135,7 @@ export const createDirectRecordToolsFactory = (deps: DirectRecordToolsDeps) => { return deps.deleteRecordService.execute({ objectName: objectMetadata.nameSingular, objectRecordId: parameters.input.id, - workspaceId: context.workspaceId, + authContext, rolePermissionConfig: context.rolePermissionConfig, soft: true, }); diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/types/record-crud-execution-context.type.ts b/packages/twenty-server/src/engine/core-modules/record-crud/types/record-crud-execution-context.type.ts index ace31e4a860..02160d92cb9 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/types/record-crud-execution-context.type.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/types/record-crud-execution-context.type.ts @@ -1,6 +1,8 @@ +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; export type RecordCrudExecutionContext = { - workspaceId: string; + authContext: WorkspaceAuthContext; rolePermissionConfig?: RolePermissionConfig; }; diff --git a/packages/twenty-server/src/engine/core-modules/tool-generator/types/tool-generator.types.ts b/packages/twenty-server/src/engine/core-modules/tool-generator/types/tool-generator.types.ts index 57ae5f52baf..20800e5854b 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-generator/types/tool-generator.types.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-generator/types/tool-generator.types.ts @@ -4,6 +4,8 @@ import { type RestrictedFieldsPermissions, } from 'twenty-shared/types'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { type ObjectMetadataForToolSchema } from 'src/engine/core-modules/record-crud/types/object-metadata-for-tool-schema.type'; import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; @@ -20,6 +22,7 @@ export type ObjectWithPermission = { // Context passed to tool factories export type ToolGeneratorContext = { workspaceId: string; + authContext?: WorkspaceAuthContext; rolePermissionConfig: RolePermissionConfig; actorContext?: ActorMetadata; }; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-provider.service.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-provider.service.ts index 352277b8337..4de5580e083 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-provider.service.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-provider.service.ts @@ -139,7 +139,7 @@ export class ToolProviderService { } private async getDatabaseTools(spec: ToolSpecification): Promise { - if (!spec.rolePermissionConfig) { + if (!spec.rolePermissionConfig || !spec.authContext) { return {}; } @@ -153,6 +153,7 @@ export class ToolProviderService { return this.perObjectToolGenerator.generate( { workspaceId: spec.workspaceId, + authContext: spec.authContext, rolePermissionConfig: spec.rolePermissionConfig, actorContext: spec.actorContext, }, diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-specification.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-specification.type.ts index 1369bd56427..9ddf48abfae 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-specification.type.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-specification.type.ts @@ -1,5 +1,7 @@ import { type ActorMetadata } from 'twenty-shared/types'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { type ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum'; import { type ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum'; import { type FlatAgentWithRoleId } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type'; @@ -9,6 +11,7 @@ export type ToolSpecification = { workspaceId: string; categories: ToolCategory[]; rolePermissionConfig?: RolePermissionConfig; + authContext?: WorkspaceAuthContext; actorContext?: ActorMetadata; agent?: FlatAgentWithRoleId | null; wrapWithErrorContext?: boolean; diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts index 3b1ce396731..8db35369c4d 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations'; import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils'; -import { type QueryRunner, IsNull, Not, Repository } from 'typeorm'; +import { type QueryRunner, IsNull, Not, type Repository } from 'typeorm'; import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; @@ -24,7 +24,7 @@ import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service'; import { AuthProviderEnum } from 'src/engine/core-modules/workspace/types/workspace.type'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; import { PermissionsException, @@ -35,7 +35,7 @@ import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-t import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; +import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { assert } from 'src/utils/assert'; import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email'; @@ -325,16 +325,18 @@ export class UserWorkspaceService extends TypeOrmQueryService { const userWorkspace = await this.userWorkspaceRepository.findOne({ where: { userId, workspaceId, }, - relations: ['twoFactorAuthenticationMethods'], + relations, }); if (!isDefined(userWorkspace)) { diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts index 686a2ea4849..07504e9bc34 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts @@ -6,10 +6,12 @@ import { generateText, jsonSchema, stepCountIs, - ToolSet, + type ToolSet, } from 'ai'; import { type ActorMetadata } from 'twenty-shared/types'; -import { Repository } from 'typeorm'; +import { type Repository } from 'typeorm'; + +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; import { ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum'; import { ToolProviderService } from 'src/engine/core-modules/tool-provider/services/tool-provider.service'; @@ -20,7 +22,7 @@ import { } from 'src/engine/metadata-modules/ai/ai-agent/agent.exception'; import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const'; import { WORKFLOW_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-system-prompts.const'; -import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; +import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; import { repairToolCall } from 'src/engine/metadata-modules/ai/ai-agent/utils/repair-tool-call.util'; import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const'; import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service'; @@ -93,11 +95,13 @@ export class AgentAsyncExecutorService { userPrompt, actorContext, rolePermissionConfig, + authContext, }: { agent: AgentEntity | null; userPrompt: string; actorContext?: ActorMetadata; rolePermissionConfig?: RolePermissionConfig; + authContext?: WorkspaceAuthContext; }): Promise { try { const registeredModel = @@ -123,6 +127,7 @@ export class AgentAsyncExecutorService { ToolCategory.NATIVE_MODEL, ], rolePermissionConfig: effectiveRoleConfig, + authContext, actorContext, agent: agent as unknown as Parameters< typeof this.toolProvider.getTools diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/services/workflow-execution-context.service.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/services/workflow-execution-context.service.ts index 73dabc2f9c2..de47591cea1 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/services/workflow-execution-context.service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/services/workflow-execution-context.service.ts @@ -1,10 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { isDefined } from 'twenty-shared/utils'; import { FieldActorSource } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; +import { type WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { type WorkflowExecutionContext } from 'src/modules/workflow/workflow-executor/types/workflow-execution-context.type'; import { WorkflowRunWorkspaceService as WorkflowRunService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; @@ -15,6 +19,7 @@ export class WorkflowExecutionContextService { private readonly workflowRunService: WorkflowRunService, private readonly userWorkspaceService: UserWorkspaceService, private readonly userRoleService: UserRoleService, + private readonly applicationService: ApplicationService, ) {} async getExecutionContext(runInfo: { @@ -30,35 +35,84 @@ export class WorkflowExecutionContextService { workflowRun.createdBy.source === FieldActorSource.MANUAL && isDefined(workflowRun.createdBy.workspaceMemberId); - let roleId: string | undefined; - if (isActingOnBehalfOfUser) { - const workspaceMember = - await this.userWorkspaceService.getWorkspaceMemberOrThrow({ - workspaceMemberId: workflowRun.createdBy.workspaceMemberId!, - workspaceId: runInfo.workspaceId, - }); - - const userWorkspace = - await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({ - userId: workspaceMember.userId, - workspaceId: runInfo.workspaceId, - }); - - roleId = await this.userRoleService.getRoleIdForUserWorkspace({ - userWorkspaceId: userWorkspace.id, - workspaceId: runInfo.workspaceId, - }); + return this.buildUserExecutionContext(workflowRun, runInfo.workspaceId); } - const rolePermissionConfig = roleId - ? { unionOf: [roleId] } - : { shouldBypassPermissionChecks: true as const }; + return this.buildApplicationExecutionContext( + workflowRun, + runInfo.workspaceId, + ); + } + + private async buildUserExecutionContext( + workflowRun: WorkflowRunWorkspaceEntity, + workspaceId: string, + ): Promise { + const workspaceMember = + await this.userWorkspaceService.getWorkspaceMemberOrThrow({ + workspaceMemberId: workflowRun.createdBy.workspaceMemberId!, + workspaceId, + }); + + const userWorkspace = + await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({ + userId: workspaceMember.userId, + workspaceId, + relations: ['workspace', 'user'], + }); + + const roleId = await this.userRoleService.getRoleIdForUserWorkspace({ + userWorkspaceId: userWorkspace.id, + workspaceId, + }); + + const authContext = { + user: userWorkspace.user, + apiKey: null, + application: null, + workspace: userWorkspace.workspace, + workspaceMemberId: workspaceMember.id, + userWorkspaceId: userWorkspace.id, + } as WorkspaceAuthContext; return { - isActingOnBehalfOfUser, + isActingOnBehalfOfUser: true, + initiator: workflowRun.createdBy, + rolePermissionConfig: { unionOf: [roleId] }, + authContext, + }; + } + + private async buildApplicationExecutionContext( + workflowRun: WorkflowRunWorkspaceEntity, + workspaceId: string, + ): Promise { + const { application, workspace } = + await this.applicationService.findTwentyStandardApplicationOrThrow( + workspaceId, + ); + + const rolePermissionConfig = isDefined( + application.defaultServerlessFunctionRoleId, + ) + ? { unionOf: [application.defaultServerlessFunctionRoleId] } + : { shouldBypassPermissionChecks: true as const }; + + const authContext = { + user: null, + apiKey: null, + application, + workspace, + workspaceMemberId: undefined, + userWorkspaceId: undefined, + } as WorkspaceAuthContext; + + return { + isActingOnBehalfOfUser: false, initiator: workflowRun.createdBy, rolePermissionConfig, + authContext, }; } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-execution-context.type.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-execution-context.type.ts index 8714f55fb88..f91457ed947 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-execution-context.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-execution-context.type.ts @@ -1,9 +1,12 @@ import { type ActorMetadata } from 'twenty-shared/types'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; export type WorkflowExecutionContext = { isActingOnBehalfOfUser: boolean; initiator: ActorMetadata; rolePermissionConfig: RolePermissionConfig; + authContext: WorkspaceAuthContext; }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module.ts index 98047c8c719..0f44eab927e 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module'; import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; @@ -13,6 +14,7 @@ import { AiAgentWorkflowAction } from './ai-agent.workflow-action'; @Module({ imports: [ + ApplicationModule, AiAgentExecutionModule, AiBillingModule, TypeOrmModule.forFeature([AgentEntity]), diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts index 971ee6ecd9a..05cfaa872fb 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { resolveInput } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; +import { type Repository } from 'typeorm'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; @@ -86,6 +86,7 @@ export class AiAgentWorkflowAction implements WorkflowAction { ? executionContext.initiator : undefined, rolePermissionConfig: executionContext.rolePermissionConfig, + authContext: executionContext.authContext, }, ); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts index c895a6738e0..ed5570038fa 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts @@ -69,7 +69,7 @@ export class CreateRecordWorkflowAction implements WorkflowAction { const toolOutput = await this.createRecordService.execute({ objectName: workflowActionInput.objectName, objectRecord: workflowActionInput.objectRecord, - workspaceId, + authContext: executionContext.authContext, createdBy, rolePermissionConfig: executionContext.rolePermissionConfig, }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts index 58b12d7ab7c..a8e858d3456 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts @@ -61,15 +61,13 @@ export class DeleteRecordWorkflowAction implements WorkflowAction { ); } - const { workspaceId } = runInfo; - const executionContext = await this.workflowExecutionContextService.getExecutionContext(runInfo); const toolOutput = await this.deleteRecordService.execute({ objectName: workflowActionInput.objectName, objectRecordId: workflowActionInput.objectRecordId, - workspaceId, + authContext: executionContext.authContext, rolePermissionConfig: executionContext.rolePermissionConfig, soft: true, }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts index 353018cba7e..583d739b5fd 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { - FieldMetadataComplexOption, - FieldMetadataDefaultOption, + type FieldMetadataComplexOption, + type FieldMetadataDefaultOption, } from 'twenty-shared/types'; import { computeRecordGqlOperationFilter, @@ -112,7 +112,7 @@ export class FindRecordsWorkflowAction implements WorkflowAction { filter: gqlOperationFilter, orderBy: workflowActionInput.orderBy?.gqlOperationOrderBy, limit: workflowActionInput.limit, - workspaceId, + authContext: executionContext.authContext, rolePermissionConfig: executionContext.rolePermissionConfig, }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module.ts index 1c58cc2e603..a7327d535af 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; +import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { RecordCrudModule } from 'src/engine/core-modules/record-crud/record-crud.module'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; @@ -14,6 +15,7 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow @Module({ imports: [ + ApplicationModule, RecordCrudModule, WorkflowRunModule, UserWorkspaceModule, diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts index ee5b31cbd7c..b37e8e470a5 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts @@ -94,7 +94,7 @@ export class UpdateRecordWorkflowAction implements WorkflowAction { objectRecordId: workflowActionInput.objectRecordId, objectRecord: workflowActionInput.objectRecord, fieldsToUpdate: workflowActionInput.fieldsToUpdate, - workspaceId, + authContext: executionContext.authContext, updatedBy, rolePermissionConfig: executionContext.rolePermissionConfig, }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts index 377c64891aa..369dc805cb2 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts @@ -18,7 +18,7 @@ import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type'; import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util'; import { isWorkflowUpsertRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-upsert-record-action.guard'; -import { WorkflowUpsertRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type'; +import { type WorkflowUpsertRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type'; @Injectable() export class UpsertRecordWorkflowAction implements WorkflowAction { @@ -57,15 +57,13 @@ export class UpsertRecordWorkflowAction implements WorkflowAction { ); } - const { workspaceId } = runInfo; - const executionContext = await this.workflowExecutionContextService.getExecutionContext(runInfo); const toolOutput = await this.upsertRecordService.execute({ objectName: workflowActionInput.objectName, objectRecord: workflowActionInput.objectRecord, - workspaceId, + authContext: executionContext.authContext, rolePermissionConfig: executionContext.rolePermissionConfig, });