From 06b193684038ce125c12f86dc5678978d320338e Mon Sep 17 00:00:00 2001 From: Dinawo Date: Wed, 17 Apr 2024 17:00:04 +0200 Subject: [PATCH] Update user data handling in UserIDMiddlewareAD.js and refactor getUserData function --- Middlewares/UserIDMiddlewareAD.js | 1 - Middlewares/authMiddleware.js | 7 --- config/logs.js | 84 ++++++++++++++++++++++++------- docker-compose.yml | 18 ++++--- files.json | 57 --------------------- models/banModel.js | 60 ++++++++++++++++++++++ models/fileCreated.js | 2 +- routes/routes.js | 23 +++++---- server.js | 29 +++++------ 9 files changed, 163 insertions(+), 118 deletions(-) delete mode 100644 files.json create mode 100644 models/banModel.js diff --git a/Middlewares/UserIDMiddlewareAD.js b/Middlewares/UserIDMiddlewareAD.js index 19eddea..cf852a8 100644 --- a/Middlewares/UserIDMiddlewareAD.js +++ b/Middlewares/UserIDMiddlewareAD.js @@ -15,7 +15,6 @@ async function getUserData() { async function checkUserExistsAD(req, res, next) { let userData = await getUserData(); - console.log('User data:', userData); if (Array.isArray(req.user)) { req.user = req.user.find(u => u._json && u._json.sAMAccountName); diff --git a/Middlewares/authMiddleware.js b/Middlewares/authMiddleware.js index 2f6b4e7..5308906 100644 --- a/Middlewares/authMiddleware.js +++ b/Middlewares/authMiddleware.js @@ -4,19 +4,14 @@ const { logger, logRequestInfo, ErrorLogger, authLogger } = require('../config/l const debug = require('debug')('app:authMiddleware'); const authMiddleware = async (req, res, next) => { - debug('Checking if user is authenticated...'); if (req.isAuthenticated() || (req.session && req.session.user && req.session.user.name)) { - debug('User is authenticated, reading user data...'); const data = await fs.promises.readFile(path.join(__dirname, '../data', 'user.json'), 'utf8'); const users = JSON.parse(data); - debug('User data:', users); const user = users.find(user => user.name === (req.session.user && req.session.user.name)); - debug('User name from session:', req.session.user && req.session.user.name); if (!user) { authLogger.info('User is not authenticated and user name is not set'); - debug('User not found in user data, redirecting to login...'); return res.redirect('/auth/login'); } @@ -25,11 +20,9 @@ const authMiddleware = async (req, res, next) => { authLogger.info(`Login successfully completed, logged in user is: id=${user.id}, name=${user.name}, role=${user.role}, IP: ${req.ip}, User Agent: ${req.headers['user-agent']}`); } - debug('Setting user data in session and locals...'); res.locals.user = user; req.session.user = user; req.userData = user; - debug('User data set, calling next middleware...'); return next(); } else { authLogger.info(`Authentication failed for IP: ${req.ip}, User Agent: ${req.headers['user-agent']}. Redirecting to login.`); diff --git a/config/logs.js b/config/logs.js index 674157d..ce141d4 100644 --- a/config/logs.js +++ b/config/logs.js @@ -3,16 +3,20 @@ const winston = require('winston'); const DailyRotateFile = require('winston-daily-rotate-file'); const logPrefix = (label) => { - if (label === 'server') { - return '[\x1b[32mServer\x1b[0m] '; - } else if (label === 'client') { - return '[\x1b[34mClient\x1b[0m] '; - } else if (label === 'Internal-Server-Error') { - return '[\x1b[38;5;9mInternal-Error\x1b[0m] '; - } else if (label === 'Authentification') { - return '[\x1b[33mAuthentification\x1b[0m] '; - } - return ''; + if (label === 'server') { + return '[🖥️ \x1b[32mServer\x1b[0m] '; + } else if (label === 'client') { + return '[🌐 \x1b[34mClient\x1b[0m] '; + } else if (label === 'Internal-Server-Error') { + return '[❗️ \x1b[38;5;9mInternal-Error\x1b[0m] '; + } else if (label === 'Authentification') { + return '[🔒 \x1b[33mAuthentification\x1b[0m] '; + } else if (label === 'Suspicious Request') { + return '[⚠️ \x1b[38;5;208mSuspicious Request\x1b[0m] '; + }else if (label === 'API Request') { + return '[⚙️ \x1b[38;5;9mAPI Request\x1b[0m] '; + } + return ''; }; const createDailyRotateFileTransport = () => { @@ -20,7 +24,7 @@ const createDailyRotateFileTransport = () => { filename: 'logs/log-%DATE%.log', datePattern: 'YYYY-MM-DD', zippedArchive: false, - maxSize: '20m', + maxSize: '20m', maxFiles: '14d' }); }; @@ -32,11 +36,11 @@ const logger = winston.createLogger({ format.printf(info => { const { timestamp, level, label, message } = info; const prefix = logPrefix(label); - return `[${timestamp}] ${prefix}${message}`; + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; }) ), transports: [ - new winston.transports.Console(), + new winston.transports.Console(), createDailyRotateFileTransport() ] }); @@ -48,11 +52,11 @@ const ErrorLogger = winston.createLogger({ format.printf(info => { const { timestamp, level, label, message } = info; const prefix = logPrefix(label); - return `[${timestamp}] ${prefix}${message}`; + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; }) ), transports: [ - new winston.transports.Console(), + new winston.transports.Console(), createDailyRotateFileTransport() ] }); @@ -64,7 +68,7 @@ const clientLogger = winston.createLogger({ format.printf(info => { const { timestamp, level, label, message } = info; const prefix = logPrefix(label); - return `[${timestamp}] ${prefix}${message}`; + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; }) ), transports: [ @@ -80,7 +84,23 @@ const authLogger = winston.createLogger({ format.printf(info => { const { timestamp, level, label, message } = info; const prefix = logPrefix(label); - return `[${timestamp}] ${prefix}${message}`; + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; + }) + ), + transports: [ + new winston.transports.Console(), + createDailyRotateFileTransport() + ] +}); + +const suspiciousLogger = winston.createLogger({ + format: format.combine( + format.label({ label: 'Suspicious Request' }), + format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + format.printf(info => { + const { timestamp, level, label, message } = info; + const prefix = logPrefix(label); + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; }) ), transports: [ @@ -92,10 +112,36 @@ const authLogger = winston.createLogger({ const logRequestInfo = (req, res, next) => { const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; const userAgent = req.headers['user-agent']; - req.log = clientLogger; + req.log = clientLogger; req.log.info(`[${ip}] - ${userAgent} - ${req.method} ${req.url}`); next(); }; +const apiLogger = winston.createLogger({ + format: format.combine( + format.label({ label: 'API Request' }), + format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + format.printf(info => { + const { timestamp, level, label, message } = info; + const prefix = logPrefix(label); + return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`; + }) + ), + transports: [ + new winston.transports.Console(), + createDailyRotateFileTransport() + ] +}); -module.exports = { logger, clientLogger, ErrorLogger, logRequestInfo, authLogger }; +const logApiRequest = (req, res, next) => { + const start = Date.now(); + res.on('finish', () => { + const duration = Date.now() - start; + const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + apiLogger.info(`[${ip}] - ${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`); + }); + next(); +}; + + +module.exports = { logger, clientLogger, ErrorLogger, logRequestInfo, authLogger, suspiciousLogger, logApiRequest }; diff --git a/docker-compose.yml b/docker-compose.yml index 0c0087e..7434eb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,12 @@ -version: '3.8' +version: '3' services: - cdn-app-insider: - image: swiftlogiclabs/cdn-app-insider:latest - restart: unless-stopped - container_name: cdn-app-container - ports: - - "5053:5053" \ No newline at end of file + app: + image: swiftlogiclabs/cdn-app-insider + volumes: + - data-volume:/app/data + - report-volume:/app/report + - cdn-files-volume:/app/cdn-files +volumes: + data-volume: + report-volume: + cdn-files-volume: \ No newline at end of file diff --git a/files.json b/files.json deleted file mode 100644 index f1a179e..0000000 --- a/files.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "routes": [ - "/routes/index.js", - "/routes/auth.js", - "/routes/dpanel.js", - "/routes/attachments.js" - ], - "controllers": [ - "/controllers/paramController.js" - ], - "models": [ - "/models/AuthUser.js", - "/models/paramModel.js", - "/models/Passport-ActiveDirectory.js", - "/models/Passport-Discord.js", - "/models/updateHelper.js", - "/models/updateManager.js", - "/models/reportManager.js", - "/models/Usermanager.js" - ], - "views": [ - "/views/AuthLogin.ejs", - "/views/dashboard.ejs", - "/views/acces-denied.ejs", - "/views/error-recovery.ejs", - "/views/file-not-found.ejs", - "/views/folder.ejs", - "/views/parametres.ejs", - "/views/unauthorized.ejs", - "/views/paramAdmin.ejs", - "/views/paramAdminPrivacy&Security.ejs", - "/views/paramAdminStats&Logs.ejs", - "/views/paramAdminSetup.ejs", - "/views/paramAdminUser.ejs", - "/views/password-check.ejs", - "/views/paramAdmin.ejs", - "/views/file-expired.ejs", - "/views/upload.ejs" - ], - "config": [ - "/config/logs.js" - ], - "Middleware": [ - "/Middleware/authMiddleware.js", - "/Middleware/checkUpdate.js", - "/Middleware/UserIDMiddlewareAD.js", - "/Middleware/UserIDMiddlewareDiscord.js" - ], - "json-file": [ - "/package.json", - "/package-lock.json", - "/user.json", - "/files.json", - "/file-info.json", - "/setup.json" - ] -} diff --git a/models/banModel.js b/models/banModel.js new file mode 100644 index 0000000..aa2d92c --- /dev/null +++ b/models/banModel.js @@ -0,0 +1,60 @@ +const fs = require('fs'); +const path = require('path'); +const { suspiciousLogger } = require('../config/logs'); + +const BAN_LEVELS = [10, 30, 60, Infinity]; +const SUSPICIOUS_REQUEST_LIMIT = 5; +const BAN_FILE_PATH = path.join(__dirname, '..', 'data', 'banUser.json'); + +const logAndBanSuspiciousActivity = async (req, res, next) => { + const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress; + const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`; + +if (req.originalUrl === '/auth/activedirectory', "/favicon.ico" && req.method === 'POST') { + next(); + return; +} + + let bans; + try { + if (!fs.existsSync(BAN_FILE_PATH)) { + fs.writeFileSync(BAN_FILE_PATH, JSON.stringify({})); + } + bans = JSON.parse(fs.readFileSync(BAN_FILE_PATH)); + } catch (err) { + bans = {}; + } + + let ban = bans[ip]; + + if (ban) { + const timeSinceLastRequest = Date.now() - ban.lastRequestTime; + if (timeSinceLastRequest < 60 * 1000) { + ban.suspiciousRequestCount++; + } else { + ban.suspiciousRequestCount = 1; + } + if (ban.suspiciousRequestCount >= SUSPICIOUS_REQUEST_LIMIT) { + ban.level++; + ban.suspiciousRequestCount = 0; + ban.banUntil = Date.now() + BAN_LEVELS[ban.level % BAN_LEVELS.length] * 60 * 1000; + } + ban.lastRequestTime = Date.now(); + } else { + bans[ip] = { level: 0, banUntil: Date.now(), suspiciousRequestCount: 1, lastRequestTime: Date.now() }; + ban = bans[ip]; + } + + fs.writeFileSync(BAN_FILE_PATH, JSON.stringify(bans)); + + if (ban && Date.now() < ban.banUntil) { + res.status(403).json({ message: 'You are banned. Please try again later.' }); + return; + } + + suspiciousLogger.info(`Suspicious request from IP: ${ip} tried to access ${req.method} ${url}`); + + next(); +}; + +module.exports = { logAndBanSuspiciousActivity }; \ No newline at end of file diff --git a/models/fileCreated.js b/models/fileCreated.js index b13aadb..2d937a2 100644 --- a/models/fileCreated.js +++ b/models/fileCreated.js @@ -3,7 +3,7 @@ const path = require('path'); const { logger, ErrorLogger, logRequestInfo } = require('../config/logs'); const dataFolderPath = path.join(__dirname, '../data'); -const filesToCreate = ['setup.json', 'user.json', 'file_info.json', 'banData.json']; +const filesToCreate = ['setup.json', 'user.json', 'file_info.json', 'banUser.json']; filesToCreate.forEach((fileName) => { const filePath = path.join(dataFolderPath, fileName); diff --git a/routes/routes.js b/routes/routes.js index b72d2db..1794653 100644 --- a/routes/routes.js +++ b/routes/routes.js @@ -1,5 +1,7 @@ const express = require('express'); const router = express.Router(); +const { logAndBanSuspiciousActivity } = require('../models/banModel.js'); +const { logApiRequest } = require('../config/logs.js'); const indexRoute = require('./index.js'); const DpanelDashboardRoute = require('./Dpanel/Dashboard/index.js'); @@ -42,21 +44,22 @@ router.use('/dpanel/dashboard/admin/settingsetup', AdminSettingSetupDpanelRoute) router.use('/dpanel/dashboard/admin/stats-logs', AdminStatsLogsDpanelRoute);; router.use('/dpanel/dashboard/admin/Privacy-Security', AdminPrivacySecurityDpanelRoute); -router.use('/api/dpanel/dashboard/newfolder', NewFolderRoute); -router.use('/api/dpanel/dashboard/rename', RenameFileRoute); -router.use('/api/dpanel/dashboard/delete', DeleteFileRoute); -router.use('/api/dpanel/dashboard/movefile', MoveFileRoute); -router.use('/api/dpanel/upload', UploadRoute); -router.use('/api/dpanel/dashboard/admin/update-role', UpdateRoleAdminRoute); -router.use('/api/dpanel/dashboard/admin/update-setup', UpdateSetupAdminRoute); -router.use('/api/dpanel/dashboard/deletefolder', DeleteFolderRoute); -router.use('/api/dpanel/dashboard/deletefile/', DeleteFileFolderRoute); +router.use('/api/dpanel/dashboard/newfolder', logApiRequest, NewFolderRoute); +router.use('/api/dpanel/dashboard/rename', logApiRequest, RenameFileRoute); +router.use('/api/dpanel/dashboard/delete', logApiRequest, DeleteFileRoute); +router.use('/api/dpanel/dashboard/movefile', logApiRequest, MoveFileRoute); +router.use('/api/dpanel/upload', logApiRequest, UploadRoute); +router.use('/api/dpanel/dashboard/admin/update-role', logApiRequest, UpdateRoleAdminRoute); +router.use('/api/dpanel/dashboard/admin/update-setup', logApiRequest, UpdateSetupAdminRoute); +router.use('/api/dpanel/dashboard/deletefolder', logApiRequest, DeleteFolderRoute); +router.use('/api/dpanel/dashboard/deletefile/', logApiRequest,DeleteFileFolderRoute); router.use('/auth/login', loginRoute); router.use('/auth/logout', logoutRoute); router.use('/auth/activedirectory', activeDirectoryRoute); router.use('/auth/discord', discordRoute); -router.use('/*', indexRoute) + +router.use('/*', logAndBanSuspiciousActivity, indexRoute); module.exports = router; \ No newline at end of file diff --git a/server.js b/server.js index 3c16c10..f8b6413 100644 --- a/server.js +++ b/server.js @@ -4,19 +4,22 @@ const passport = require('passport'); const bodyParser = require('body-parser'); const { logger, logRequestInfo, ErrorLogger } = require('./config/logs'); const path = require("path"); -require('dotenv').config(); const { version } = require('./package.json'); const axios = require('axios'); -const app = express(); const flash = require('connect-flash'); const crypto = require('crypto'); const fs = require('fs'); const SystemReport = require('./models/reportManager.js'); +const routes = require('./routes/routes.js'); +const cron = require('node-cron'); + +require('dotenv').config(); +const app = express(); require('./models/fileCreated.js'); let setup; try { - setup = JSON.parse(fs.readFileSync(path.join('/data', 'setup.json'), 'utf8')); + setup = JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'setup.json'), 'utf8')); } catch (err) { console.error('Error reading setup.json:', err); process.exit(1); @@ -33,8 +36,10 @@ if (setup.ldap !== undefined) { app.use(express.static(path.join(__dirname, 'public'))); app.get(['/data/user.json', '/data/file_info.json', '/data/setup.json'], (req, res) => { -res.status(403).json({ error: 'Access Denied. You do not have permission to access this resource.' }); -});app.use(express.urlencoded({ extended: true })); + res.status(403).json({ error: 'Access Denied. You do not have permission to access this resource.' }); +}); + +app.use(express.urlencoded({ extended: true })); function generateSecretKey() { return crypto.randomBytes(64).toString('hex'); @@ -54,7 +59,6 @@ app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(flash()); -const routes = require('./routes/routes.js'); app.use('/public', express.static(path.join(__dirname, 'public'))); app.set('view engine', 'ejs'); app.set('views', __dirname + '/views'); @@ -62,13 +66,12 @@ app.use(routes); app.use(logRequestInfo); -const cron = require('node-cron'); cron.schedule('00 03 * * *', async () => { try { const report = await SystemReport.generate(); if (report !== null) { - logger.info('System error report generated successfully'); + logger.info('System error report generated successfully'); } } catch (err) { ErrorLogger.error('Error generating report :', err); @@ -77,7 +80,7 @@ cron.schedule('00 03 * * *', async () => { cron.schedule('0 * * * *', async () => { try { - const fileInfoData = await fs.promises.readFile(path.join(__dirname,'/data/', 'file_info.json'), 'utf8'); + const fileInfoData = await fs.promises.readFile(path.join(__dirname, '/data/', 'file_info.json'), 'utf8'); const fileInfo = JSON.parse(fileInfoData); const now = new Date(); @@ -112,15 +115,9 @@ app.listen(PORT, () => { console.clear(); if (logger) { logger.info(`🚀 Your server is available and running on port ${PORT}`); - logger.info(`⚜️ Application developed by Dinawo, part of the SwiftLogic Labs group`); + logger.info(`⚜️ Application developed by Dinawo, part of the SwiftLogic Labs group`); logger.info(`♨️ Version: ${version}`); console.log(''); - const filesData = fs.readFileSync('files.json', 'utf8'); - const files = JSON.parse(filesData); - - const numberOfFiles = Object.values(files).flat().length; - - logger.info(`🔰 Number of files activated during server startup: ${numberOfFiles}`); } else { console.error('🔴 Logger is not initialized'); }