| 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'}`, |
| }); |
| } |
| }; |
| }; |
|
|