mirror of
https://github.com/FlowiseAI/Flowise.git
synced 2026-10-12 02:02:17 +00:00
Fix Flowise 788 Improve Tenant Validation (#6620)
* fix(flowise-788): improve tenant validation * fix: use updatedBy instead of createdBy in organization user update
This commit is contained in:
@@ -22,6 +22,11 @@ import { assertQueryOrganizationMatchesActiveOrg, getLoggedInUser, userMayManage
|
||||
export class OrganizationUserController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.organizationId = req.user.activeOrganizationId
|
||||
req.body.createdBy = req.user.id
|
||||
|
||||
const organizationUserservice = new OrganizationUserService()
|
||||
const totalOrgUsers = await organizationUserservice.readOrgUsersCountByOrgId(req.body.organizationId)
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
@@ -102,6 +107,11 @@ export class OrganizationUserController {
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.organizationId = req.user.activeOrganizationId
|
||||
req.body.updatedBy = req.user.id
|
||||
|
||||
const organizationUserService = new OrganizationUserService()
|
||||
const organizationUser = await organizationUserService.updateOrganizationUser(req.body)
|
||||
return res.status(StatusCodes.OK).json(organizationUser)
|
||||
@@ -116,10 +126,16 @@ export class OrganizationUserController {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
const currentPlatform = getRunningExpressApp().identityManager.getPlatformType()
|
||||
await queryRunner.connect()
|
||||
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
const query = req.query as Partial<OrganizationUser>
|
||||
if (!query.organizationId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Organization ID is required')
|
||||
}
|
||||
if (query.organizationId !== req.user.activeOrganizationId) {
|
||||
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
|
||||
}
|
||||
if (!query.userId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'User ID is required')
|
||||
}
|
||||
@@ -138,7 +154,11 @@ export class OrganizationUserController {
|
||||
})
|
||||
if (personalWorkspaces.length === 1)
|
||||
// delete personal workspace
|
||||
await workspaceService.deleteWorkspaceById(queryRunner, personalWorkspaces[0].workspaceId)
|
||||
await workspaceService.deleteWorkspaceById(
|
||||
queryRunner,
|
||||
personalWorkspaces[0].workspaceId,
|
||||
req.user.activeOrganizationId
|
||||
)
|
||||
// remove user from other workspces
|
||||
organizationUser = await organizationUserService.deleteOrganizationUser(queryRunner, query.organizationId, query.userId)
|
||||
// soft delete user because they might workspace might created by them
|
||||
|
||||
@@ -4,7 +4,7 @@ import { QueryRunner } from 'typeorm'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { WorkspaceUser } from '../database/entities/workspace-user.entity'
|
||||
import { WorkspaceUser, WorkspaceUserStatus } from '../database/entities/workspace-user.entity'
|
||||
import { WorkspaceUserService } from '../services/workspace-user.service'
|
||||
import {
|
||||
assertMayReadTargetUser,
|
||||
@@ -17,8 +17,13 @@ import {
|
||||
export class WorkspaceUserController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.createdBy = req.user.id
|
||||
req.body.status = WorkspaceUserStatus.INVITED
|
||||
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const newWorkspaceUser = await workspaceUserService.createWorkspaceUser(req.body)
|
||||
const newWorkspaceUser = await workspaceUserService.createWorkspaceUser(req.body, req.user.activeOrganizationId)
|
||||
return res.status(StatusCodes.CREATED).json(newWorkspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -92,10 +97,14 @@ export class WorkspaceUserController {
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner: QueryRunner | undefined
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.updatedBy = req.user.id
|
||||
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const workspaceUser = await workspaceUserService.updateWorkspaceUser(req.body, queryRunner)
|
||||
const workspaceUser = await workspaceUserService.updateWorkspaceUser(req.body, queryRunner, req.user.activeOrganizationId)
|
||||
return res.status(StatusCodes.OK).json(workspaceUser)
|
||||
} catch (error) {
|
||||
if (queryRunner && queryRunner.isTransactionActive) await queryRunner.rollbackTransaction()
|
||||
@@ -107,10 +116,16 @@ export class WorkspaceUserController {
|
||||
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
const query = req.query as Partial<WorkspaceUser>
|
||||
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const workspaceUser = await workspaceUserService.deleteWorkspaceUser(query.workspaceId, query.userId)
|
||||
const workspaceUser = await workspaceUserService.deleteWorkspaceUser(
|
||||
query.workspaceId,
|
||||
query.userId,
|
||||
req.user.activeOrganizationId
|
||||
)
|
||||
return res.status(StatusCodes.OK).json(workspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -20,6 +20,12 @@ import { assertQueryOrganizationMatchesActiveOrg, assertWorkspaceIdAccessibleToU
|
||||
export class WorkspaceController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.organizationId = req.user.activeOrganizationId
|
||||
req.body.createdBy = req.user.id
|
||||
req.body.existingWorkspaceId = req.body.existingWorkspaceId ? req.user.activeWorkspaceId : undefined
|
||||
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const newWorkspace = await workspaceUserService.createWorkspace(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newWorkspace)
|
||||
@@ -135,7 +141,7 @@ export class WorkspaceController {
|
||||
activeOrganizationSubscriptionId: subscriptionId,
|
||||
activeOrganizationCustomerId: customerId,
|
||||
activeOrganizationProductId: productId,
|
||||
isOrganizationAdmin: workspaceUser.roleId === ownerRole.id,
|
||||
isOrganizationAdmin: organizationUser.roleId === ownerRole.id,
|
||||
activeWorkspaceId: workspace.id,
|
||||
activeWorkspace: workspace.name,
|
||||
assignedWorkspaces,
|
||||
@@ -179,6 +185,11 @@ export class WorkspaceController {
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
req.body.organizationId = req.user.activeOrganizationId
|
||||
req.body.updatedBy = req.user.id
|
||||
|
||||
const workspaceService = new WorkspaceService()
|
||||
const workspace = await workspaceService.updateWorkspace(req.body)
|
||||
return res.status(StatusCodes.OK).json(workspace)
|
||||
@@ -190,6 +201,8 @@ export class WorkspaceController {
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner: QueryRunner | undefined
|
||||
try {
|
||||
if (!req.user) throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const workspaceId = req.params.id
|
||||
@@ -199,7 +212,7 @@ export class WorkspaceController {
|
||||
const workspaceService = new WorkspaceService()
|
||||
await queryRunner.startTransaction()
|
||||
|
||||
const workspace = await workspaceService.deleteWorkspaceById(queryRunner, workspaceId)
|
||||
const workspace = await workspaceService.deleteWorkspaceById(queryRunner, workspaceId, req.user.activeOrganizationId)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
return res.status(StatusCodes.OK).json(workspace)
|
||||
|
||||
@@ -143,7 +143,7 @@ export const initializeJwtCookieMiddleware = async (app: express.Application, id
|
||||
if (!organizationUser)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, OrganizationUserErrorMessage.ORGANIZATION_USER_NOT_FOUND)
|
||||
organizationUser.status = OrganizationUserStatus.ACTIVE
|
||||
await workspaceUserService.updateWorkspaceUser(workspaceUser, queryRunner)
|
||||
await workspaceUserService.updateWorkspaceUser(workspaceUser, queryRunner, organizationUser.organizationId)
|
||||
await organizationUserService.updateOrganizationUser(organizationUser)
|
||||
|
||||
const workspaceUsers = await workspaceUserService.readWorkspaceUserByUserId(organizationUser.userId, queryRunner)
|
||||
@@ -180,7 +180,7 @@ export const initializeJwtCookieMiddleware = async (app: express.Application, id
|
||||
activeOrganizationSubscriptionId: subscriptionId,
|
||||
activeOrganizationCustomerId: customerId,
|
||||
activeOrganizationProductId: productId,
|
||||
isOrganizationAdmin: workspaceUser.roleId === ownerRole.id,
|
||||
isOrganizationAdmin: organizationUser.roleId === ownerRole.id,
|
||||
activeWorkspaceId: workspaceUser.workspaceId,
|
||||
activeWorkspace: workspaceUser.workspace.name,
|
||||
assignedWorkspaces,
|
||||
|
||||
@@ -510,7 +510,7 @@ export class AccountService {
|
||||
|
||||
const personalWorkspaceRole = await this.roleService.readGeneralRoleByName(GeneralRole.PERSONAL_WORKSPACE, queryRunner)
|
||||
if (oldWorkspaceUser && oldWorkspaceUser.roleId !== personalWorkspaceRole.id) {
|
||||
await this.workspaceUserService.deleteWorkspaceUser(oldWorkspaceUser.workspaceId, user.id)
|
||||
await this.workspaceUserService.deleteWorkspaceUser(oldWorkspaceUser.workspaceId, user.id, data.workspace.organizationId)
|
||||
}
|
||||
|
||||
await queryRunner.startTransaction()
|
||||
|
||||
@@ -222,6 +222,8 @@ export class OrganizationUserService {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, OrganizationUserErrorMessage.ORGANIZATION_USER_ALREADY_EXISTS)
|
||||
const role = await this.roleService.readRoleIsGeneral(data.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
if (role.name === GeneralRole.OWNER)
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, RoleErrorMessage.INVALID_ROLE_PERMISSIONS)
|
||||
const createdBy = await this.userService.readUserById(data.createdBy, queryRunner)
|
||||
if (!createdBy) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
|
||||
|
||||
@@ -290,6 +292,8 @@ export class OrganizationUserService {
|
||||
if (newOrganizationUser.roleId) {
|
||||
const role = await this.roleService.readRoleIsGeneral(newOrganizationUser.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
if (role.name === GeneralRole.OWNER && organizationUser.roleId !== newOrganizationUser.roleId)
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, RoleErrorMessage.INVALID_ROLE_PERMISSIONS)
|
||||
}
|
||||
|
||||
if (newOrganizationUser.status) this.validateOrganizationUserStatus(newOrganizationUser.status)
|
||||
|
||||
@@ -222,11 +222,13 @@ export class WorkspaceUserService {
|
||||
return await queryRunner.manager.save(WorkspaceUser, data)
|
||||
}
|
||||
|
||||
public async createWorkspaceUser(data: Partial<WorkspaceUser>) {
|
||||
public async createWorkspaceUser(data: Partial<WorkspaceUser>, activeOrganizationId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
|
||||
const { workspace, workspaceUser } = await this.readWorkspaceUserByWorkspaceIdUserId(data.workspaceId, data.userId, queryRunner)
|
||||
if (workspace.organizationId !== activeOrganizationId)
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
if (workspaceUser) throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, WorkspaceUserErrorMessage.WORKSPACE_USER_ALREADY_EXISTS)
|
||||
const role = await this.roleService.readRoleById(data.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
@@ -330,13 +332,16 @@ export class WorkspaceUserService {
|
||||
return newWorkspace
|
||||
}
|
||||
|
||||
public async updateWorkspaceUser(newWorkspaserUser: Partial<WorkspaceUser>, queryRunner: QueryRunner) {
|
||||
public async updateWorkspaceUser(newWorkspaserUser: Partial<WorkspaceUser>, queryRunner: QueryRunner, activeOrganizationId: string) {
|
||||
const { workspaceUser } = await this.readWorkspaceUserByWorkspaceIdUserId(
|
||||
newWorkspaserUser.workspaceId,
|
||||
newWorkspaserUser.userId,
|
||||
queryRunner
|
||||
)
|
||||
if (!workspaceUser) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceUserErrorMessage.WORKSPACE_USER_NOT_FOUND)
|
||||
const workspace = await this.workspaceService.readWorkspaceById(workspaceUser.workspaceId, queryRunner)
|
||||
if (!workspace || workspace.organizationId !== activeOrganizationId)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
if (newWorkspaserUser.roleId && workspaceUser.role) {
|
||||
const role = await this.roleService.readRoleById(newWorkspaserUser.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
@@ -359,12 +364,18 @@ export class WorkspaceUserService {
|
||||
return updataWorkspaceUser
|
||||
}
|
||||
|
||||
public async deleteWorkspaceUser(workspaceId: string | undefined, userId: string | undefined) {
|
||||
public async deleteWorkspaceUser(
|
||||
workspaceId: string | undefined,
|
||||
userId: string | undefined,
|
||||
activeOrganizationId: string | undefined
|
||||
) {
|
||||
const queryRunner = this.dataSource.createQueryRunner()
|
||||
try {
|
||||
await queryRunner.connect()
|
||||
const { workspace, workspaceUser } = await this.readWorkspaceUserByWorkspaceIdUserId(workspaceId, userId, queryRunner)
|
||||
if (!workspaceUser) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceUserErrorMessage.WORKSPACE_USER_NOT_FOUND)
|
||||
if (!workspace || workspace.organizationId !== activeOrganizationId)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
const role = await this.roleService.readRoleById(workspaceUser.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
if (role.name === GeneralRole.OWNER)
|
||||
|
||||
@@ -137,7 +137,8 @@ export class WorkspaceService {
|
||||
await queryRunner.connect()
|
||||
|
||||
const oldWorkspaceData = await this.readWorkspaceById(newWorkspaceData.id, queryRunner)
|
||||
if (!oldWorkspaceData) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
if (!oldWorkspaceData || oldWorkspaceData.organizationId !== newWorkspaceData.organizationId)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
const user = await this.userService.readUserById(newWorkspaceData.updatedBy, queryRunner)
|
||||
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
|
||||
if (newWorkspaceData.name) {
|
||||
@@ -161,9 +162,10 @@ export class WorkspaceService {
|
||||
return updateWorkspace
|
||||
}
|
||||
|
||||
public async deleteWorkspaceById(queryRunner: QueryRunner, workspaceId: string) {
|
||||
public async deleteWorkspaceById(queryRunner: QueryRunner, workspaceId: string, activeOrganizationId: string) {
|
||||
const workspace = await this.readWorkspaceById(workspaceId, queryRunner)
|
||||
if (!workspace) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
if (!workspace || workspace.organizationId !== activeOrganizationId)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
|
||||
// First get all related entities that need to be deleted
|
||||
const chatflows = await queryRunner.manager.findBy(ChatFlow, { workspaceId })
|
||||
|
||||
@@ -57,7 +57,14 @@ export const WHITELIST_URLS = [
|
||||
GithubSSO.CALLBACK_URI
|
||||
]
|
||||
|
||||
export const API_KEY_BLACKLIST_URLS = ['/api/v1/nvidia-nim', '/api/v1/account/delete', '/api/v1/files']
|
||||
export const API_KEY_BLACKLIST_URLS = [
|
||||
'/api/v1/nvidia-nim',
|
||||
'/api/v1/account/delete',
|
||||
'/api/v1/files',
|
||||
'/api/v1/organizationuser',
|
||||
'/api/v1/workspace',
|
||||
'/api/v1/workspaceuser'
|
||||
]
|
||||
|
||||
export const enum GeneralErrorMessage {
|
||||
FORBIDDEN = 'Forbidden',
|
||||
|
||||
Reference in New Issue
Block a user