From 78144f37b5752984cfa12c2a7254a5f403f45016 Mon Sep 17 00:00:00 2001 From: Ong Chung Yau <33013947+chungyau97@users.noreply.github.com> Date: Tue, 5 Aug 2025 01:57:28 +0800 Subject: [PATCH] Feature update public-chatflow access control (#4978) * feat: update public-chatflow access control * chore: deprecate getSinglePublicChatflow method * chore: remove RequireAuthIfNotpublic --- .../server/src/controllers/chatflows/index.ts | 21 +++++++++++++-- .../server/src/services/chatflows/index.ts | 26 ------------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/packages/server/src/controllers/chatflows/index.ts b/packages/server/src/controllers/chatflows/index.ts index 000a2770..f8a10c33 100644 --- a/packages/server/src/controllers/chatflows/index.ts +++ b/packages/server/src/controllers/chatflows/index.ts @@ -9,6 +9,9 @@ import { getRunningExpressApp } from '../../utils/getRunningExpressApp' import { checkUsageLimit } from '../../utils/quotaUsage' import { RateLimiterManager } from '../../utils/rateLimit' import { getPageAndLimitParams } from '../../utils/pagination' +import { WorkspaceUserErrorMessage, WorkspaceUserService } from '../../enterprise/services/workspace-user.service' +import { QueryRunner } from 'typeorm' +import { GeneralErrorMessage } from '../../utils/constants' const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => { try { @@ -197,6 +200,7 @@ const updateChatflow = async (req: Request, res: Response, next: NextFunction) = } const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFunction) => { + let queryRunner: QueryRunner | undefined try { if (typeof req.params === 'undefined' || !req.params.id) { throw new InternalFlowiseError( @@ -204,10 +208,23 @@ const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFu `Error: chatflowsController.getSinglePublicChatflow - id not provided!` ) } - const apiResponse = await chatflowsService.getSinglePublicChatflow(req.params.id) - return res.json(apiResponse) + const chatflow = await chatflowsService.getChatflowById(req.params.id) + if (!chatflow) return res.status(StatusCodes.NOT_FOUND).json({ message: 'Chatflow not found' }) + if (chatflow.isPublic) return res.status(StatusCodes.OK).json(chatflow) + if (!req.user) return res.status(StatusCodes.UNAUTHORIZED).json({ message: GeneralErrorMessage.UNAUTHORIZED }) + queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner() + const workspaceUserService = new WorkspaceUserService() + const workspaceUser = await workspaceUserService.readWorkspaceUserByUserId(req.user.id, queryRunner) + if (workspaceUser.length === 0) + return res.status(StatusCodes.NOT_FOUND).json({ message: WorkspaceUserErrorMessage.WORKSPACE_USER_NOT_FOUND }) + const workspaceIds = workspaceUser.map((user) => user.workspaceId) + if (!workspaceIds.includes(chatflow.workspaceId)) + return res.status(StatusCodes.BAD_REQUEST).json({ message: 'You are not in the workspace that owns this chatflow' }) + return res.status(StatusCodes.OK).json(chatflow) } catch (error) { next(error) + } finally { + if (queryRunner) await queryRunner.release() } } diff --git a/packages/server/src/services/chatflows/index.ts b/packages/server/src/services/chatflows/index.ts index 9ce0a529..a525168d 100644 --- a/packages/server/src/services/chatflows/index.ts +++ b/packages/server/src/services/chatflows/index.ts @@ -339,31 +339,6 @@ const updateChatflow = async ( } } -// Get specific chatflow via id (PUBLIC endpoint, used when sharing chatbot link) -const getSinglePublicChatflow = async (chatflowId: string): Promise => { - try { - const appServer = getRunningExpressApp() - const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ - id: chatflowId - }) - if (dbResponse && dbResponse.isPublic) { - return dbResponse - } else if (dbResponse && !dbResponse.isPublic) { - throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `Unauthorized`) - } - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`) - } catch (error) { - if (error instanceof InternalFlowiseError && error.statusCode === StatusCodes.UNAUTHORIZED) { - throw error - } else { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: chatflowsService.getSinglePublicChatflow - ${getErrorMessage(error)}` - ) - } - } -} - // Get specific chatflow chatbotConfig via id (PUBLIC endpoint, used to retrieve config for embedded chat) // Safe as public endpoint as chatbotConfig doesn't contain sensitive credential const getSinglePublicChatbotConfig = async (chatflowId: string): Promise => { @@ -438,7 +413,6 @@ export default { getChatflowById, saveChatflow, updateChatflow, - getSinglePublicChatflow, getSinglePublicChatbotConfig, checkIfChatflowHasChanged, getAllChatflowsCountByOrganization