| const { logger } = require('@librechat/data-schemas'); |
| const { SystemRoles } = require('librechat-data-provider'); |
| const { checkPermission } = require('~/server/services/PermissionService'); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const canAccessResource = (options) => { |
| const { |
| resourceType, |
| requiredPermission, |
| resourceIdParam = 'resourceId', |
| idResolver = null, |
| } = options; |
|
|
| if (!resourceType || typeof resourceType !== 'string') { |
| throw new Error('canAccessResource: resourceType is required and must be a string'); |
| } |
|
|
| if (!requiredPermission || typeof requiredPermission !== 'number') { |
| throw new Error('canAccessResource: requiredPermission is required and must be a number'); |
| } |
|
|
| return async (req, res, next) => { |
| try { |
| |
| const rawResourceId = req.params[resourceIdParam]; |
|
|
| if (!rawResourceId) { |
| logger.warn(`[canAccessResource] Missing ${resourceIdParam} in route parameters`); |
| return res.status(400).json({ |
| error: 'Bad Request', |
| message: `${resourceIdParam} is required`, |
| }); |
| } |
|
|
| |
| if (!req.user || !req.user.id) { |
| logger.warn( |
| `[canAccessResource] Unauthenticated request for ${resourceType} ${rawResourceId}`, |
| ); |
| return res.status(401).json({ |
| error: 'Unauthorized', |
| message: 'Authentication required', |
| }); |
| } |
| |
| if (req.user.role === SystemRoles.ADMIN) { |
| return next(); |
| } |
| const userId = req.user.id; |
| let resourceId = rawResourceId; |
| let resourceInfo = null; |
|
|
| |
| if (idResolver) { |
| logger.debug( |
| `[canAccessResource] Resolving ${resourceType} custom ID ${rawResourceId} to ObjectId`, |
| ); |
|
|
| const resolutionResult = await idResolver(rawResourceId); |
|
|
| if (!resolutionResult) { |
| logger.warn(`[canAccessResource] ${resourceType} not found: ${rawResourceId}`); |
| return res.status(404).json({ |
| error: 'Not Found', |
| message: `${resourceType} not found`, |
| }); |
| } |
|
|
| |
| if (typeof resolutionResult === 'string' || resolutionResult._id) { |
| resourceId = resolutionResult._id || resolutionResult; |
| resourceInfo = typeof resolutionResult === 'object' ? resolutionResult : null; |
| } else { |
| resourceId = resolutionResult; |
| } |
|
|
| logger.debug( |
| `[canAccessResource] Resolved ${resourceType} ${rawResourceId} to ObjectId ${resourceId}`, |
| ); |
| } |
|
|
| |
| const hasPermission = await checkPermission({ |
| userId, |
| role: req.user.role, |
| resourceType, |
| resourceId, |
| requiredPermission, |
| }); |
|
|
| if (hasPermission) { |
| logger.debug( |
| `[canAccessResource] User ${userId} has permission ${requiredPermission} on ${resourceType} ${rawResourceId} (${resourceId})`, |
| ); |
|
|
| req.resourceAccess = { |
| resourceType, |
| resourceId, |
| customResourceId: rawResourceId, |
| permission: requiredPermission, |
| userId, |
| ...(resourceInfo && { resourceInfo }), |
| }; |
|
|
| return next(); |
| } |
|
|
| logger.warn( |
| `[canAccessResource] User ${userId} denied access to ${resourceType} ${rawResourceId} ` + |
| `(required permission: ${requiredPermission})`, |
| ); |
|
|
| return res.status(403).json({ |
| error: 'Forbidden', |
| message: `Insufficient permissions to access this ${resourceType}`, |
| }); |
| } catch (error) { |
| logger.error(`[canAccessResource] Error checking access for ${resourceType}:`, error); |
| return res.status(500).json({ |
| error: 'Internal Server Error', |
| message: 'Failed to check resource access permissions', |
| }); |
| } |
| }; |
| }; |
|
|
| module.exports = { |
| canAccessResource, |
| }; |
|
|