Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/server/src/controllers/chat-messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
)
}
const chatflowid = req.params.id
const chatflow = await chatflowsService.getChatflowById(req.params.id, workspaceId)
const chatflow = await chatflowsService.getChatflowByIdForWorkspace(req.params.id, workspaceId)
if (!chatflow) {
return res.status(404).send('Chatflow not found')
}
Expand Down
9 changes: 8 additions & 1 deletion packages/server/src/controllers/chatflows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,14 @@ const checkIfChatflowHasChanged = async (req: Request, res: Response, next: Next
`Error: chatflowsController.checkIfChatflowHasChanged - lastUpdatedDateTime not provided!`
)
}
const apiResponse = await chatflowsService.checkIfChatflowHasChanged(req.params.id, req.params.lastUpdatedDateTime)
const workspaceId = req.user?.activeWorkspaceId
if (!workspaceId) {
throw new InternalFlowiseError(
StatusCodes.NOT_FOUND,
`Error: chatflowsController.checkIfChatflowHasChanged - workspace ${workspaceId} not found!`
Comment thread
0xi4o marked this conversation as resolved.
Outdated
)
}
const apiResponse = await chatflowsService.checkIfChatflowHasChanged(req.params.id, req.params.lastUpdatedDateTime, workspaceId)
return res.json(apiResponse)
} catch (error) {
next(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const createInternalPrediction = async (req: Request, res: Response, next: NextF
try {
const workspaceId = req.user?.activeWorkspaceId

const chatflow = await chatflowService.getChatflowById(req.params.id, workspaceId)
const chatflow = await chatflowService.getChatflowByIdForWorkspace(req.params.id, workspaceId)
if (!chatflow) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${req.params.id} not found`)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/controllers/predictions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const createPrediction = async (req: Request, res: Response, next: NextFunction)
}
const workspaceId = req.user?.activeWorkspaceId

const chatflow = await chatflowsService.getChatflowById(req.params.id, workspaceId)
const chatflow = await chatflowsService.getChatflowByIdForWorkspace(req.params.id, workspaceId)
Comment thread
0xi4o marked this conversation as resolved.
Outdated
if (!chatflow) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${req.params.id} not found`)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { WorkspaceUser } from '../database/entities/workspace-user.entity'
import { OrganizationUserService } from '../services/organization-user.service'
import { RoleService } from '../services/role.service'
import { WorkspaceService } from '../services/workspace.service'
import { assertQueryOrganizationMatchesActiveOrg, getLoggedInUser, userMayManageOrgUsers } from '../utils/tenantRequestGuards'

export class OrganizationUserController {
public async create(req: Request, res: Response, next: NextFunction) {
Expand All @@ -35,11 +36,14 @@ export class OrganizationUserController {
public async read(req: Request, res: Response, next: NextFunction) {
let queryRunner
try {
const user = getLoggedInUser(req)
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
await queryRunner.connect()
const query = req.query as OrganizationUserQuery
const organizationUserservice = new OrganizationUserService()

assertQueryOrganizationMatchesActiveOrg(user, query.organizationId)

let organizationUser:
| {
organization: Organization
Expand All @@ -58,15 +62,32 @@ export class OrganizationUserController {
queryRunner
)
} else if (query.organizationId && query.roleId) {
if (!userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationIdRoleId(
query.organizationId,
query.roleId,
queryRunner
)
} else if (query.organizationId) {
if (!userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationId(query.organizationId, queryRunner)
} else if (query.userId) {
organizationUser = await organizationUserservice.readOrganizationUserByUserId(query.userId, queryRunner)
if (query.userId === user.id) {
organizationUser = await organizationUserservice.readOrganizationUserByUserId(query.userId, queryRunner)
} else {
if (!userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationIdUserId(
user.activeOrganizationId,
query.userId,
queryRunner
)
}
} else {
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
}
Expand Down
19 changes: 17 additions & 2 deletions packages/server/src/enterprise/controllers/role.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { NextFunction, Request, Response } from 'express'
import { StatusCodes } from 'http-status-codes'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { Role } from '../database/entities/role.entity'
import { RoleService } from '../services/role.service'
import { RoleErrorMessage, RoleService } from '../services/role.service'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { GeneralErrorMessage } from '../../utils/constants'
import { getLoggedInUser } from '../utils/tenantRequestGuards'

export class RoleController {
public async create(req: Request, res: Response, next: NextFunction) {
Expand All @@ -19,15 +21,28 @@ export class RoleController {
public async read(req: Request, res: Response, next: NextFunction) {
let queryRunner
try {
const user = getLoggedInUser(req)
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
await queryRunner.connect()
const query = req.query as Partial<Role>
const roleService = new RoleService()

let role: Role | Role[] | null | (Role & { userCount: number })[]
if (query.id) {
role = await roleService.readRoleById(query.id, queryRunner)
const oneRole = await roleService.readRoleById(query.id, queryRunner)
if (!oneRole) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
}
if (oneRole.organizationId != null) {
if (!user.activeOrganizationId || oneRole.organizationId !== user.activeOrganizationId) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
}
role = oneRole
} else if (query.organizationId) {
if (!user.activeOrganizationId || query.organizationId !== user.activeOrganizationId) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
role = await roleService.readRoleByOrganizationId(query.organizationId, queryRunner)
} else {
role = await roleService.readRoleByGeneral(queryRunner)
Expand Down
22 changes: 20 additions & 2 deletions packages/server/src/enterprise/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { GeneralErrorMessage } from '../../utils/constants'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { User } from '../database/entities/user.entity'
import { LoggedInUser } from '../Interface.Enterprise'
import { AccountService } from '../services/account.service'
import { UserErrorMessage, UserService } from '../services/user.service'
import { assertMayReadTargetUser } from '../utils/tenantRequestGuards'

export class UserController {
public async create(req: Request, res: Response, next: NextFunction) {
Expand All @@ -23,16 +25,32 @@ export class UserController {
try {
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
await queryRunner.connect()

const sessionUser = req.user as LoggedInUser | undefined
if (!sessionUser) {
throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, UserErrorMessage.USER_NOT_FOUND)
}

const query = req.query as Partial<User>
const userService = new UserService()

let user: User | null
if (query.id) {
await assertMayReadTargetUser(sessionUser, query.id, queryRunner)
user = await userService.readUserById(query.id, queryRunner)
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
} else if (query.email) {
user = await userService.readUserByEmail(query.email, queryRunner)
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
const emailLc = query.email.trim().toLowerCase()
Comment thread
0xi4o marked this conversation as resolved.
Outdated
const selfEmail = sessionUser.email?.trim().toLowerCase()
if (!selfEmail || emailLc !== selfEmail) {
const byEmail = await userService.readUserByEmail(query.email, queryRunner)
if (!byEmail) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
await assertMayReadTargetUser(sessionUser, byEmail.id, queryRunner)
user = byEmail
} else {
user = await userService.readUserByEmail(query.email, queryRunner)
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
}
} else {
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import { GeneralErrorMessage } from '../../utils/constants'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { WorkspaceUser } from '../database/entities/workspace-user.entity'
import { WorkspaceUserService } from '../services/workspace-user.service'
import {
assertQueryOrganizationMatchesActiveOrg,
assertWorkspaceIdAccessibleToUser,
getLoggedInUser,
userMayManageOrgUsers
} from '../utils/tenantRequestGuards'

export class WorkspaceUserController {
public async create(req: Request, res: Response, next: NextFunction) {
Expand All @@ -21,19 +27,24 @@ export class WorkspaceUserController {
public async read(req: Request, res: Response, next: NextFunction) {
let queryRunner
try {
const user = getLoggedInUser(req)
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
await queryRunner.connect()
const query = req.query as Partial<WorkspaceUser & { organizationId: string | undefined }>
const workspaceUserService = new WorkspaceUserService()

assertQueryOrganizationMatchesActiveOrg(user, query.organizationId)

let workspaceUser: any
if (query.workspaceId && query.userId) {
await assertWorkspaceIdAccessibleToUser(user, query.workspaceId, queryRunner)
workspaceUser = await workspaceUserService.readWorkspaceUserByWorkspaceIdUserId(
query.workspaceId,
query.userId,
queryRunner
)
} else if (query.workspaceId) {
await assertWorkspaceIdAccessibleToUser(user, query.workspaceId, queryRunner)
workspaceUser = await workspaceUserService.readWorkspaceUserByWorkspaceId(query.workspaceId, queryRunner)
} else if (query.organizationId && query.userId) {
Comment thread
0xi4o marked this conversation as resolved.
workspaceUser = await workspaceUserService.readWorkspaceUserByOrganizationIdUserId(
Expand All @@ -42,9 +53,15 @@ export class WorkspaceUserController {
queryRunner
)
} else if (query.userId) {
if (typeof query.userId === 'string' && query.userId !== user.id && !userMayManageOrgUsers(user)) {
Comment thread
0xi4o marked this conversation as resolved.
Outdated
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
workspaceUser = await workspaceUserService.readWorkspaceUserByUserId(query.userId, queryRunner)
} else if (query.roleId) {
workspaceUser = await workspaceUserService.readWorkspaceUserByRoleId(query.roleId, queryRunner)
if (!userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
workspaceUser = await workspaceUserService.readWorkspaceUserByRoleId(query.roleId, queryRunner, user.activeOrganizationId)
} else {
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
}
Expand Down
10 changes: 10 additions & 0 deletions packages/server/src/enterprise/controllers/workspace.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { RoleErrorMessage, RoleService } from '../services/role.service'
import { UserErrorMessage, UserService } from '../services/user.service'
import { WorkspaceUserErrorMessage, WorkspaceUserService } from '../services/workspace-user.service'
import { WorkspaceErrorMessage, WorkspaceService } from '../services/workspace.service'
import { assertQueryOrganizationMatchesActiveOrg, assertWorkspaceIdAccessibleToUser, getLoggedInUser } from '../utils/tenantRequestGuards'

export class WorkspaceController {
public async create(req: Request, res: Response, next: NextFunction) {
Expand All @@ -30,21 +31,30 @@ export class WorkspaceController {
public async read(req: Request, res: Response, next: NextFunction) {
let queryRunner
try {
const user = getLoggedInUser(req)
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
await queryRunner.connect()
const query = req.query as Partial<Workspace>
const workspaceService = new WorkspaceService()

assertQueryOrganizationMatchesActiveOrg(user, query.organizationId)

let workspace:
| Workspace
| null
| (Workspace & {
userCount: number
})[]
if (query.id) {
await assertWorkspaceIdAccessibleToUser(user, query.id, queryRunner)
workspace = await workspaceService.readWorkspaceById(query.id, queryRunner)
} else if (query.organizationId) {
workspace = await workspaceService.readWorkspaceByOrganizationId(query.organizationId, queryRunner)
if (!user.isOrganizationAdmin && Array.isArray(workspace)) {
const allowed = new Set((user.assignedWorkspaces ?? []).map((w) => w.id))
if (user.activeWorkspaceId) allowed.add(user.activeWorkspaceId)
workspace = workspace.filter((w) => allowed.has(w.id))
}
} else {
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/enterprise/routes/workspace-user.route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import express from 'express'
import { WorkspaceUserController } from '../controllers/workspace-user.controller'
import { IdentityManager } from '../../IdentityManager'
import { checkPermission } from '../rbac/PermissionCheck'
import { checkAnyPermission, checkPermission } from '../rbac/PermissionCheck'

const router = express.Router()
const workspaceUserController = new WorkspaceUserController()

// no feature flag because user with lower plan can read invited workspaces with higher plan
router.get('/', workspaceUserController.read)
router.get('/', checkAnyPermission('workspace:add-user,workspace:unlink-user,users:manage'), workspaceUserController.read)

router.post(
'/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,21 @@ export class WorkspaceUserService {
}))
}

public async readWorkspaceUserByRoleId(roleId: string | undefined, queryRunner: QueryRunner) {
public async readWorkspaceUserByRoleId(roleId: string | undefined, queryRunner: QueryRunner, organizationId?: string | undefined) {
const role = await this.roleService.readRoleById(roleId, queryRunner)
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
const ownerRole = await this.roleService.readGeneralRoleByName(GeneralRole.OWNER, queryRunner)

const workspaceUsers = await queryRunner.manager
const qb = queryRunner.manager
.createQueryBuilder(WorkspaceUser, 'workspaceUser')
.innerJoinAndSelect('workspaceUser.workspace', 'workspace')
.innerJoinAndSelect('workspaceUser.user', 'user')
.innerJoinAndSelect('workspaceUser.role', 'role')
.where('workspaceUser.roleId = :roleId', { roleId })
.getMany()
if (organizationId) {
qb.andWhere('workspace.organizationId = :organizationId', { organizationId })
}
const workspaceUsers = await qb.getMany()

return workspaceUsers.map((workspaceUser) => {
delete workspaceUser.user.credential
Expand Down
Loading
Loading