Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions nodejs/src/controller/web/brainController.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,53 @@ const deleteBrain = catchAsync(async (req, res) => {
})

const deleteAllBrain = catchAsync(async (req, res) => {
// Feature flag check
if (process.env.BULK_DELETE_BRAINS_ENABLED !== 'true') {
return res.status(403).json({ error: 'Bulk delete is disabled by feature flag.' });
}

// Confirm param check
if (req.query.confirm !== 'true' && req.body.confirm !== true) {
return res.status(400).json({ error: 'Explicit confirmation required (confirm=true).' });
}

// Role/permission defense-in-depth
const allowedRoles = ['COMPANY', 'MANAGER'];
const userRoles = req.user.roles || [];
const userPermissions = req.user.permissions || [];
if (!userRoles.some(role => allowedRoles.includes(role)) || !userPermissions.includes('brain.delete_all')) {
return res.status(403).json({ error: 'Forbidden: insufficient permissions.' });
}

// Rate limiting (simple in-memory, replace with Redis for prod)
if (!global.bulkDeleteRateLimit) global.bulkDeleteRateLimit = {};
const userId = req.userId;
const now = Date.now();
const windowMs = 60 * 60 * 1000; // 1 hour
const maxDeletes = 2;
const userLimit = global.bulkDeleteRateLimit[userId] || { count: 0, last: 0 };
if (userLimit.last + windowMs > now) {
if (userLimit.count >= maxDeletes) {
return res.status(429).json({ error: 'Rate limit exceeded for bulk delete.' });
}
userLimit.count++;
} else {
userLimit.count = 1;
userLimit.last = now;
}
global.bulkDeleteRateLimit[userId] = userLimit;

// Audit logging
const logger = require('../../utils/logger');
logger.info(`Bulk delete brains requested by user ${userId} (${req.user.email}) at ${new Date().toISOString()}`);

const result = await brainService.deleteAllBrain(req);
if (result) {
logger.info(`Bulk delete brains SUCCESS for user ${userId} (${req.user.email}) at ${new Date().toISOString()}`);
res.message = _localize('module.delete', req, BRAIN);
return util.successResponse(result, res);
}
logger.error(`Bulk delete brains FAILURE for user ${userId} (${req.user.email}) at ${new Date().toISOString()}`);
return util.failureResponse(_localize('module.deleteError', req, BRAIN), res);
})

Expand Down
22 changes: 22 additions & 0 deletions nodejs/src/middleware/authorization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Authorization middleware for role/permission checks
module.exports = function checkPermission(permission) {
return function (req, res, next) {
const user = req.user;
// Feature flag: disable endpoint if not enabled
if (process.env.BULK_DELETE_BRAINS_ENABLED !== 'true') {
return res.status(403).json({ error: 'Bulk delete is disabled by feature flag.' });
}
// Check user and permissions
if (!user || !user.permissions || !user.roles) {
return res.status(403).json({ error: 'Unauthorized: missing user roles/permissions.' });
}
// Only COMPANY or MANAGER roles allowed
const allowedRoles = ['COMPANY', 'MANAGER'];
const hasRole = user.roles.some(role => allowedRoles.includes(role));
const hasPermission = user.permissions.includes(permission);
if (hasRole && hasPermission) {
return next();
}
return res.status(403).json({ error: 'Forbidden: insufficient permissions.' });
};
};
3 changes: 2 additions & 1 deletion nodejs/src/routes/web/brains.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ const brainController = require('../../controller/web/brainController');
const { createBrainKeys, updateBrainKeys, shareBrainKeys, unshareBrainKeys, shareDocKeys } = require('../../utils/validations/brain');
const { partialUpdateKeys } = require('../../utils/validations/common');
const { authentication } = require('../../middleware/authentication');
const checkPermission = require('../../middleware/authorization');
const { checkPromptLimit } = require('../../middleware/promptlimit');

router.post('/create', validate(createBrainKeys), authentication,checkPromptLimit, brainController.createBrain);
router.put('/update/:id', validate(updateBrainKeys), authentication,checkPromptLimit, brainController.updateBrain);
router.get('/:slug', authentication, brainController.getBrain);
router.delete('/delete/:id', authentication, brainController.deleteBrain);
router.delete('/deleteall', authentication, brainController.deleteAllBrain);
router.delete('/deleteall', authentication, checkPermission('brain.delete_all'), brainController.deleteAllBrain);
router.post('/list', authentication, checkPromptLimit, brainController.getAll);
router.patch('/partial/:slug', validate(partialUpdateKeys), authentication, brainController.partialUpdate);
router.post('/unshare', validate(unshareBrainKeys), authentication, brainController.unShareBrain);
Expand Down