UNPKG

@ferjssilva/fast-crud-api

Version:

A complete and fast crud API generator

103 lines (89 loc) 3.16 kB
/** * User-scoped resource middleware * Automatically filters and secures resources by user ID * @param {Object} model - Mongoose model * @param {String} modelName - Model collection name * @param {Array} userScopedResources - Array of resource names that should be user-scoped */ function createUserScopeHandler(model, modelName, userScopedResources = []) { // Skip if userScoped is not provided, is null, or is empty if (!userScopedResources || userScopedResources.length === 0) { return null; } // Check if this model is in the userScoped list if (!userScopedResources.includes(modelName)) { return null; } // Return the preHandler hook return async function userScopePreHandler(request, reply) { const method = request.method; // Check authentication for all user-scoped operations if (!request.userId) { reply.code(401).send({ error: 'Unauthorized', message: 'Authentication required' }); return; } // Handle GET requests if (method === 'GET') { // Check if user is trying to access other users' data via query params if (request.query.userId && request.query.userId !== request.userId) { reply.code(403).send({ error: 'Forbidden', message: 'Cannot access other users\' data' }); return; } // Automatically inject userId filter request.query.userId = request.userId; } // Handle POST requests if (method === 'POST') { // Check if user is trying to set a different userId if (request.body.userId && request.body.userId !== request.userId) { reply.code(403).send({ error: 'Forbidden', message: 'Cannot modify other users\' data' }); return; } // Automatically inject userId request.body.userId = request.userId; } // Handle PUT requests if (method === 'PUT') { // Check if user is trying to set a different userId if (request.body.userId && request.body.userId !== request.userId) { reply.code(403).send({ error: 'Forbidden', message: 'Cannot modify other users\' data' }); return; } // Automatically inject userId (will be used in the update) request.body.userId = request.userId; // Note: Ownership verification is handled atomically in the route handler // to prevent race conditions (TOCTOU vulnerabilities) } // Handle DELETE requests // Note: Ownership verification is handled atomically in the route handler // to prevent race conditions. No additional checks needed here. }; } /** * Check if a resource is user-scoped * @param {String} modelName - Model collection name * @param {Array} userScopedResources - Array of user-scoped resource names * @returns {Boolean} True if the resource is user-scoped */ function isUserScoped(modelName, userScopedResources = []) { if (!userScopedResources || userScopedResources.length === 0) { return false; } return userScopedResources.includes(modelName); } module.exports = { createUserScopeHandler, isUserScoped };