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); } /** * Core function to check if a user has one or more required permissions * @param user - The user object * @param permissionType - The type of permission to check * @param permissions - The list of specific permissions to check * @param bodyProps - An optional object where keys are permissions and values are arrays of properties to check * @param checkObject - The object to check properties against * @param skipCheck - An optional function that takes the checkObject and returns true to skip permission checking * @returns Whether the user has the required permissions */ export const checkAccess = async ({ req, user, permissionType, permissions, getRoleByName, bodyProps = {} as Record, checkObject = {}, skipCheck, }: { user: IUser; req?: ServerRequest; permissionType: PermissionTypes; permissions: Permissions[]; bodyProps?: Record; checkObject?: object; /** If skipCheck function is provided and returns true, skip permission checking */ skipCheck?: (req?: ServerRequest) => boolean; getRoleByName: (roleName: string, fieldsToSelect?: string | string[]) => Promise; }): Promise => { 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; }; /** * Middleware to check if a user has one or more required permissions, optionally based on `req.body` properties. * @param permissionType - The type of permission to check. * @param permissions - The list of specific permissions to check. * @param bodyProps - An optional object where keys are permissions and values are arrays of `req.body` properties to check. * @param skipCheck - An optional function that takes req.body and returns true to skip permission checking. * @param getRoleByName - A function to get the role by name. * @returns Express middleware function. */ export const generateCheckAccess = ({ permissionType, permissions, bodyProps = {} as Record, skipCheck, getRoleByName, }: { permissionType: PermissionTypes; permissions: Permissions[]; bodyProps?: Record; skipCheck?: (req?: ServerRequest) => boolean; getRoleByName: (roleName: string, fieldsToSelect?: string | string[]) => Promise; }): ((req: ServerRequest, res: ServerResponse, next: NextFunction) => Promise) => { 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?.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'}`, }); } }; };