Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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 - active workspace ID not found!'
)
}
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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have we tested this on chatembed, API etc? prediction is a public request and does not contain any info about workspace

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 && !userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
if (query.userId === user.id) {
organizationUser = await organizationUserservice.readOrganizationUserByUserId(query.userId, queryRunner)
} else {
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 = (typeof query.email === 'string' ? query.email : '').trim().toLowerCase()
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,30 +27,44 @@ 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.
if (query.userId !== user.id && !userMayManageOrgUsers(user)) {
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, GeneralErrorMessage.FORBIDDEN)
}
workspaceUser = await workspaceUserService.readWorkspaceUserByOrganizationIdUserId(
query.organizationId,
query.userId,
queryRunner
)
} else if (query.userId) {
if (query.userId !== user.id && !userMayManageOrgUsers(user)) {
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
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