Update v1.2.0-beta - Dynamic context menu & permissions
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
✨ New Features: - Dynamic permission-based context menus for files and folders - Support for collaborative folder access control - Upload to specific folders including shared folders - Changelog modal for version updates - Improved dark mode synchronization 🐛 Bug Fixes: - Fixed context menu displaying incorrect options - Fixed CSS !important override preventing dynamic menu behavior - Fixed folder collaboration permission checks - Fixed breadcrumb navigation with empty segments - Fixed "Premature close" error loop in attachments - Fixed missing user variable in admin routes - Fixed avatar loading COEP policy issues 🔒 Security: - Added security middleware (CSRF, rate limiting, input validation) - Fixed collaboration folder access validation - Improved shared folder permission handling 🎨 UI/UX Improvements: - Removed Actions column from folder view - Context menu now properly hides/shows based on permissions - Better visual feedback for collaborative folders - Improved upload flow with inline modals 🧹 Code Quality: - Added collaboration data to folder routes - Refactored context menu logic for better maintainability - Added debug logging for troubleshooting - Improved file upload handling with chunking support
This commit is contained in:
170
Middlewares/csrfProtectionMiddleware.js
Normal file
170
Middlewares/csrfProtectionMiddleware.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Middleware de protection CSRF (Cross-Site Request Forgery)
|
||||
* Génère et valide des tokens CSRF pour les requêtes sensibles
|
||||
*/
|
||||
|
||||
const crypto = require('crypto');
|
||||
const { logger } = require('../config/logs');
|
||||
|
||||
// Stockage en mémoire des tokens CSRF (en production, utiliser Redis ou une base de données)
|
||||
const csrfTokens = new Map();
|
||||
|
||||
// Durée de vie d'un token CSRF (30 minutes)
|
||||
const TOKEN_LIFETIME = 30 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Génère un token CSRF pour une session
|
||||
*/
|
||||
const generateCsrfToken = (sessionId) => {
|
||||
const token = crypto.randomBytes(32).toString('hex');
|
||||
const expiresAt = Date.now() + TOKEN_LIFETIME;
|
||||
|
||||
csrfTokens.set(sessionId, {
|
||||
token,
|
||||
expiresAt
|
||||
});
|
||||
|
||||
// Nettoyer les tokens expirés toutes les 5 minutes
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [id, data] of csrfTokens.entries()) {
|
||||
if (data.expiresAt < now) {
|
||||
csrfTokens.delete(id);
|
||||
}
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
|
||||
return token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Valide un token CSRF
|
||||
*/
|
||||
const validateCsrfToken = (sessionId, token) => {
|
||||
const storedData = csrfTokens.get(sessionId);
|
||||
|
||||
if (!storedData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (storedData.expiresAt < Date.now()) {
|
||||
csrfTokens.delete(sessionId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return storedData.token === token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware pour ajouter le token CSRF à la requête
|
||||
*/
|
||||
const csrfTokenMiddleware = (req, res, next) => {
|
||||
// Générer un ID de session si nécessaire
|
||||
if (!req.session) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const sessionId = req.session.id || req.sessionID;
|
||||
|
||||
// Générer ou récupérer le token CSRF
|
||||
let csrfToken = null;
|
||||
const storedData = csrfTokens.get(sessionId);
|
||||
|
||||
if (storedData && storedData.expiresAt > Date.now()) {
|
||||
csrfToken = storedData.token;
|
||||
} else {
|
||||
csrfToken = generateCsrfToken(sessionId);
|
||||
}
|
||||
|
||||
// Ajouter le token aux locals pour l'utiliser dans les vues
|
||||
res.locals.csrfToken = csrfToken;
|
||||
|
||||
// Ajouter une méthode helper
|
||||
req.csrfToken = () => csrfToken;
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware de validation CSRF pour les méthodes POST, PUT, DELETE
|
||||
*/
|
||||
const csrfProtectionMiddleware = (req, res, next) => {
|
||||
// Ignorer les requêtes GET, HEAD, OPTIONS
|
||||
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Ignorer certaines routes (API publiques, webhooks, etc.)
|
||||
const exemptPaths = [
|
||||
'/auth/activedirectory/callback',
|
||||
'/auth/discord/callback',
|
||||
'/api/webhook'
|
||||
];
|
||||
|
||||
if (exemptPaths.some(path => req.path.startsWith(path))) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Vérifier si la session existe
|
||||
if (!req.session) {
|
||||
logger.warn(`CSRF protection: No session found for ${req.method} ${req.path} from ${req.ip}`);
|
||||
return res.status(403).json({
|
||||
error: 'Session invalide',
|
||||
code: 'NO_SESSION'
|
||||
});
|
||||
}
|
||||
|
||||
const sessionId = req.session.id || req.sessionID;
|
||||
|
||||
// Récupérer le token CSRF de la requête
|
||||
const token = req.body._csrf ||
|
||||
req.query._csrf ||
|
||||
req.headers['x-csrf-token'] ||
|
||||
req.headers['csrf-token'];
|
||||
|
||||
if (!token) {
|
||||
logger.warn(`CSRF protection: No token provided for ${req.method} ${req.path} from ${req.ip}`);
|
||||
return res.status(403).json({
|
||||
error: 'Token CSRF manquant',
|
||||
code: 'CSRF_TOKEN_MISSING'
|
||||
});
|
||||
}
|
||||
|
||||
// Valider le token
|
||||
if (!validateCsrfToken(sessionId, token)) {
|
||||
logger.warn(`CSRF protection: Invalid token for ${req.method} ${req.path} from ${req.ip}`);
|
||||
return res.status(403).json({
|
||||
error: 'Token CSRF invalide ou expiré',
|
||||
code: 'CSRF_TOKEN_INVALID'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* Route pour obtenir un nouveau token CSRF
|
||||
*/
|
||||
const getCsrfToken = (req, res) => {
|
||||
const sessionId = req.session?.id || req.sessionID;
|
||||
|
||||
if (!sessionId) {
|
||||
return res.status(400).json({
|
||||
error: 'Session invalide'
|
||||
});
|
||||
}
|
||||
|
||||
const token = generateCsrfToken(sessionId);
|
||||
|
||||
res.json({
|
||||
csrfToken: token
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
csrfTokenMiddleware,
|
||||
csrfProtectionMiddleware,
|
||||
getCsrfToken,
|
||||
generateCsrfToken,
|
||||
validateCsrfToken
|
||||
};
|
||||
Reference in New Issue
Block a user