| | 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, |
| | }; |
| |
|