Feature update public-chatflow access control (#4978)

* feat: update public-chatflow access control

* chore: deprecate getSinglePublicChatflow method

* chore: remove RequireAuthIfNotpublic
This commit is contained in:
Ong Chung Yau
2025-08-05 01:57:28 +08:00
committed by GitHub
parent e3e4d6a904
commit 78144f37b5
2 changed files with 19 additions and 28 deletions
@@ -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()
}
}
@@ -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<any> => {
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<any> => {
@@ -438,7 +413,6 @@ export default {
getChatflowById,
saveChatflow,
updateChatflow,
getSinglePublicChatflow,
getSinglePublicChatbotConfig,
checkIfChatflowHasChanged,
getAllChatflowsCountByOrganization