| | import { logger } from '@librechat/data-schemas'; |
| | import { |
| | Permissions, |
| | EndpointURLs, |
| | EModelEndpoint, |
| | PermissionTypes, |
| | isAgentsEndpoint, |
| | } from 'librechat-data-provider'; |
| | import type { NextFunction, Request as ServerRequest, Response as ServerResponse } from 'express'; |
| | import type { IRole, IUser } from '@librechat/data-schemas'; |
| |
|
| | export function skipAgentCheck(req?: ServerRequest): boolean { |
| | if (!req || !req?.body?.endpoint) { |
| | return false; |
| | } |
| |
|
| | if (req.method !== 'POST') { |
| | return false; |
| | } |
| |
|
| | if (!req.originalUrl?.includes(EndpointURLs[EModelEndpoint.agents])) { |
| | return false; |
| | } |
| | return !isAgentsEndpoint(req.body.endpoint); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export const checkAccess = async ({ |
| | req, |
| | user, |
| | permissionType, |
| | permissions, |
| | getRoleByName, |
| | bodyProps = {} as Record<Permissions, string[]>, |
| | checkObject = {}, |
| | skipCheck, |
| | }: { |
| | user: IUser; |
| | req?: ServerRequest; |
| | permissionType: PermissionTypes; |
| | permissions: Permissions[]; |
| | bodyProps?: Record<Permissions, string[]>; |
| | checkObject?: object; |
| | |
| | skipCheck?: (req?: ServerRequest) => boolean; |
| | getRoleByName: (roleName: string, fieldsToSelect?: string | string[]) => Promise<IRole | null>; |
| | }): Promise<boolean> => { |
| | if (skipCheck && skipCheck(req)) { |
| | return true; |
| | } |
| |
|
| | if (!user || !user.role) { |
| | return false; |
| | } |
| |
|
| | const role = await getRoleByName(user.role); |
| | const permissionValue = role?.permissions?.[permissionType as keyof typeof role.permissions]; |
| | if (role && role.permissions && permissionValue) { |
| | const hasAnyPermission = permissions.every((permission) => { |
| | if (permissionValue[permission as keyof typeof permissionValue]) { |
| | return true; |
| | } |
| |
|
| | if (bodyProps[permission] && checkObject) { |
| | return bodyProps[permission].every((prop) => |
| | Object.prototype.hasOwnProperty.call(checkObject, prop), |
| | ); |
| | } |
| |
|
| | return false; |
| | }); |
| |
|
| | return hasAnyPermission; |
| | } |
| |
|
| | return false; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export const generateCheckAccess = ({ |
| | permissionType, |
| | permissions, |
| | bodyProps = {} as Record<Permissions, string[]>, |
| | skipCheck, |
| | getRoleByName, |
| | }: { |
| | permissionType: PermissionTypes; |
| | permissions: Permissions[]; |
| | bodyProps?: Record<Permissions, string[]>; |
| | skipCheck?: (req?: ServerRequest) => boolean; |
| | getRoleByName: (roleName: string, fieldsToSelect?: string | string[]) => Promise<IRole | null>; |
| | }): ((req: ServerRequest, res: ServerResponse, next: NextFunction) => Promise<unknown>) => { |
| | return async (req, res, next) => { |
| | try { |
| | const hasAccess = await checkAccess({ |
| | req, |
| | user: req.user as IUser, |
| | permissionType, |
| | permissions, |
| | bodyProps, |
| | checkObject: req.body, |
| | skipCheck, |
| | getRoleByName, |
| | }); |
| |
|
| | if (hasAccess) { |
| | return next(); |
| | } |
| |
|
| | logger.warn( |
| | `[${permissionType}] Forbidden: "${req.originalUrl}" - Insufficient permissions for User ${(req.user as IUser)?.id}: ${permissions.join(', ')}`, |
| | ); |
| | return res.status(403).json({ message: 'Forbidden: Insufficient permissions' }); |
| | } catch (error) { |
| | logger.error(error); |
| | return res.status(500).json({ |
| | message: `Server error: ${error instanceof Error ? error.message : 'Unknown error'}`, |
| | }); |
| | } |
| | }; |
| | }; |
| |
|