mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
* chore: Handle optional token_endpoint in OAuth metadata discovery * chore: Simplify permission typing logic in checkAccess function * feat: Implement `deleteMistralFile` function and integrate file cleanup in `uploadMistralOCR`
138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
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<Permissions, string[]>,
|
|
checkObject = {},
|
|
skipCheck,
|
|
}: {
|
|
user: IUser;
|
|
req?: ServerRequest;
|
|
permissionType: PermissionTypes;
|
|
permissions: Permissions[];
|
|
bodyProps?: Record<Permissions, string[]>;
|
|
checkObject?: object;
|
|
/** If skipCheck function is provided and returns true, skip permission checking */
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* 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<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?.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'}`,
|
|
});
|
|
}
|
|
};
|
|
};
|