From bb773ffa710bd22639c4ba2643413a0ea2b679d3 Mon Sep 17 00:00:00 2001 From: yau-wd Date: Mon, 6 Jul 2026 12:30:01 +0800 Subject: [PATCH] Fix Flowise 722 node load method workspace (#6593) fix(flowise-722):add node load method workspace scope --- .../server/src/controllers/nodes/index.ts | 3 +- .../src/routes/node-load-methods/index.ts | 10 +++++- .../server/src/services/credentials/index.ts | 32 ++++++++++++++++++- packages/server/src/services/nodes/index.ts | 22 +++++++++---- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/packages/server/src/controllers/nodes/index.ts b/packages/server/src/controllers/nodes/index.ts index 0f0327f7196..66804153667 100644 --- a/packages/server/src/controllers/nodes/index.ts +++ b/packages/server/src/controllers/nodes/index.ts @@ -77,7 +77,8 @@ const getSingleNodeAsyncOptions = async (req: Request, res: Response, next: Next } const body = req.body body.searchOptions = getWorkspaceSearchOptionsFromReq(req) - const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, body) + const workspaceId = req.user?.activeWorkspaceId + const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, body, workspaceId) return res.json(apiResponse) } catch (error) { next(error) diff --git a/packages/server/src/routes/node-load-methods/index.ts b/packages/server/src/routes/node-load-methods/index.ts index 317fd81cfa1..1f6d4cc157e 100644 --- a/packages/server/src/routes/node-load-methods/index.ts +++ b/packages/server/src/routes/node-load-methods/index.ts @@ -1,7 +1,15 @@ import express from 'express' import nodesRouter from '../../controllers/nodes' +import { checkAnyPermission } from '../../enterprise/rbac/PermissionCheck' + const router = express.Router() -router.post(['/', '/:name'], nodesRouter.getSingleNodeAsyncOptions) +router.post( + ['/', '/:name'], + checkAnyPermission( + 'chatflows:view,chatflows:create,chatflows:update,chatflows:delete,agentflows:view,agentflows:create,agentflows:update,agentflows:delete,documentStores:view,documentStores:create,documentStores:update,documentStores:add-loader' + ), + nodesRouter.getSingleNodeAsyncOptions +) export default router diff --git a/packages/server/src/services/credentials/index.ts b/packages/server/src/services/credentials/index.ts index 5a54bc56038..c0ef994002a 100644 --- a/packages/server/src/services/credentials/index.ts +++ b/packages/server/src/services/credentials/index.ts @@ -192,6 +192,35 @@ const updateCredential = async (credentialId: string, requestBody: any, workspac } } +/** + * Confirms a credential exists and belongs to (or is shared with) the given workspace. + * Does NOT decrypt or return credential material — only used for authorization checks. + * Throws 400 when workspaceId is missing (prevents unscoped lookup), 404 when the + * credential does not belong to the workspace or is not shared with it. + */ +const assertCredentialInWorkspace = async (credentialId: string, workspaceId: string | undefined): Promise => { + if (!workspaceId) { + throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, `Workspace ID is required`) + } + const appServer = getRunningExpressApp() + const owned = await appServer.AppDataSource.getRepository(Credential).findOneBy({ + id: credentialId, + workspaceId: workspaceId + }) + if (owned) return + + const shared = await appServer.AppDataSource.getRepository(WorkspaceShared).count({ + where: { + workspaceId: workspaceId, + sharedItemId: credentialId, + itemType: 'credential' + } + }) + if (shared > 0) return + + throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found`) +} + const revealCredentialById = async (credentialId: string, workspaceId: string): Promise => { try { const appServer = getRunningExpressApp() @@ -227,5 +256,6 @@ export default { getAllCredentials, getCredentialById, revealCredentialById, - updateCredential + updateCredential, + assertCredentialInWorkspace } diff --git a/packages/server/src/services/nodes/index.ts b/packages/server/src/services/nodes/index.ts index 9b78debd746..f1f87c08c8c 100644 --- a/packages/server/src/services/nodes/index.ts +++ b/packages/server/src/services/nodes/index.ts @@ -1,14 +1,15 @@ -import { cloneDeep, omit } from 'lodash' -import { StatusCodes } from 'http-status-codes' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { INodeData, MODE } from '../../Interface' import { ClientType, INodeOptionsValue } from 'flowise-components' -import { databaseEntities } from '../../utils' -import logger from '../../utils/logger' +import { StatusCodes } from 'http-status-codes' +import { cloneDeep, omit } from 'lodash' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' +import { INodeData, MODE } from '../../Interface' +import { databaseEntities } from '../../utils' import { OMIT_QUEUE_JOB_DATA } from '../../utils/constants' import { executeCustomNodeFunction } from '../../utils/executeCustomNodeFunction' +import { getRunningExpressApp } from '../../utils/getRunningExpressApp' +import logger from '../../utils/logger' +import credentialsService from '../credentials' import { filterNodeByClient } from './filterNodeByClient' export { filterNodeByClient } @@ -91,11 +92,15 @@ const getSingleNodeIcon = async (nodeName: string) => { } } -const getSingleNodeAsyncOptions = async (nodeName: string, requestBody: any): Promise => { +const getSingleNodeAsyncOptions = async (nodeName: string, requestBody: any, workspaceId?: string): Promise => { try { const appServer = getRunningExpressApp() const nodeData: INodeData = requestBody if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, nodeName)) { + if (nodeData.credential) { + await credentialsService.assertCredentialInWorkspace(nodeData.credential, workspaceId) + } + try { const nodeInstance = appServer.nodesPool.componentNodes[nodeName] const methodName = nodeData.loadMethod || '' @@ -118,6 +123,9 @@ const getSingleNodeAsyncOptions = async (nodeName: string, requestBody: any): Pr throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Node ${nodeName} not found`) } } catch (error) { + if (error instanceof InternalFlowiseError) { + throw error + } throw new InternalFlowiseError( StatusCodes.INTERNAL_SERVER_ERROR, `Error: nodesService.getSingleNodeAsyncOptions - ${getErrorMessage(error)}`