From 7f4eac1654ad832392a2b2eece97edb8d9b275b4 Mon Sep 17 00:00:00 2001 From: Chris Brame Date: Mon, 16 May 2022 17:37:47 -0400 Subject: [PATCH] refactor(account): security enhancement --- src/controllers/api/v1/users.js | 10 ++++- src/controllers/api/v2/accounts.js | 10 ++++- src/controllers/main.js | 8 ++-- src/models/session.js | 69 ++++++++++++++++++++++++++++++ src/passport/index.js | 32 ++++---------- 5 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 src/models/session.js diff --git a/src/controllers/api/v1/users.js b/src/controllers/api/v1/users.js index 9440a14c5..50fd5d95e 100644 --- a/src/controllers/api/v1/users.js +++ b/src/controllers/api/v1/users.js @@ -369,6 +369,8 @@ apiUsers.update = function (req, res) { const data = req.body // saveGroups - Profile saving where groups are not sent const saveGroups = !_.isUndefined(data.saveGroups) ? data.saveGroups : true + let passwordUpdated = false + const obj = { fullname: data.aFullname, title: data.aTitle, @@ -402,6 +404,7 @@ apiUsers.update = function (req, res) { ) { if (obj.password === obj.passconfirm) { user.password = obj.password + passwordUpdated = true } } @@ -476,7 +479,7 @@ apiUsers.update = function (req, res) { } } }, - function (err, results) { + async function (err, results) { if (err) { winston.debug(err) return res.status(400).json({ success: false, error: err }) @@ -487,6 +490,11 @@ apiUsers.update = function (req, res) { return { _id: g._id, name: g.name } }) + if (passwordUpdated) { + const Session = require('../../../models/session') + await Session.destroy(user._id) + } + return res.json({ success: true, user: user }) } ) diff --git a/src/controllers/api/v2/accounts.js b/src/controllers/api/v2/accounts.js index 75a59f74d..05a9dbeca 100644 --- a/src/controllers/api/v2/accounts.js +++ b/src/controllers/api/v2/accounts.js @@ -246,6 +246,8 @@ accountsApi.update = function (req, res) { var postData = req.body if (!username || !postData) return apiUtil.sendApiError_InvalidPostData(res) + let passwordUpdated = false + async.series( { user: function (next) { @@ -263,6 +265,7 @@ accountsApi.update = function (req, res) { ) { if (postData.password === postData.passwordConfirm) { user.password = postData.password + passwordUpdated = true } } @@ -389,7 +392,7 @@ accountsApi.update = function (req, res) { Department.getUserDepartments(postData._id, next) } }, - function (err, results) { + async function (err, results) { if (err) return apiUtil.sendApiError(res, 500, err.message) var user = results.user.toJSON() @@ -407,6 +410,11 @@ accountsApi.update = function (req, res) { }) } + if (passwordUpdated) { + const Session = require('../../../models/session') + await Session.destroy(user._id) + } + return apiUtil.sendApiSuccess(res, { user: user }) } ) diff --git a/src/controllers/main.js b/src/controllers/main.js index 979bd3ad6..62c4e840c 100644 --- a/src/controllers/main.js +++ b/src/controllers/main.js @@ -189,10 +189,12 @@ mainController.l2AuthPost = function (req, res, next) { } mainController.logout = function (req, res) { - req.logout() req.session.l2auth = null - req.session.destroy() - return res.redirect('/') + req.session.destroy(function () { + req.logout() + res.clearCookie('connect.sid') + return res.redirect('/') + }) } mainController.forgotL2Auth = function (req, res) { diff --git a/src/models/session.js b/src/models/session.js new file mode 100644 index 000000000..81f079bb6 --- /dev/null +++ b/src/models/session.js @@ -0,0 +1,69 @@ +/* + * . .o8 oooo + * .o8 "888 `888 + * .o888oo oooo d8b oooo oooo .oooo888 .ooooo. .oooo.o 888 oooo + * 888 `888""8P `888 `888 d88' `888 d88' `88b d88( "8 888 .8P' + * 888 888 888 888 888 888 888ooo888 `"Y88b. 888888. + * 888 . 888 888 888 888 888 888 .o o. )88b 888 `88b. + * "888" d888b `V88V"V8P' `Y8bod88P" `Y8bod8P' 8""888P' o888o o888o + * ======================================================================== + * Author: Chris Brame + * Updated: 5/16/22 3:01 PM + * Copyright (c) 2014-2022. All rights reserved. + */ +// This is used to connect to MongoStore for express-session to destroy the sessions of users + +const mongoose = require('mongoose') +const winston = require('../logger') + +const COLLECTION = 'sessions' + +const SessionSchema = new mongoose.Schema( + { + _id: String, + expires: Date, + session: String + }, + { strict: false } +) + +SessionSchema.statics.getAllSessionUsers = async function () {} + +SessionSchema.statics.destroyUserSession = async function (userId) { + return new Promise((resolve, reject) => { + ;(async () => { + try { + if (!userId) return reject(new Error('Invalid User Id')) + + const userSessions = await this.model(COLLECTION).find({}) + + if (userSessions) { + for (const s of userSessions) { + const id = s._id + const sessionObject = JSON.parse(s.session) + + if ( + sessionObject.passport && + sessionObject.passport.user && + sessionObject.passport.user === userId.toString() + ) { + delete sessionObject.passport + await this.model(COLLECTION).findOneAndUpdate({ _id: id }, { session: JSON.stringify(sessionObject) }) + } + } + + return resolve() + } else { + return resolve() + } + } catch (e) { + winston.error(e) + return reject(e) + } + })() + }) +} + +SessionSchema.statics.destroy = SessionSchema.statics.destroyUserSession + +module.exports = mongoose.model(COLLECTION, SessionSchema) diff --git a/src/passport/index.js b/src/passport/index.js index 90c799e5e..fe5771b5b 100644 --- a/src/passport/index.js +++ b/src/passport/index.js @@ -12,14 +12,14 @@ * Copyright (c) 2014-2019. All rights reserved. */ -var passport = require('passport') -var Local = require('passport-local').Strategy -var TotpStrategy = require('passport-totp').Strategy -var JwtStrategy = require('passport-jwt').Strategy -var ExtractJwt = require('passport-jwt').ExtractJwt -var base32 = require('thirty-two') -var User = require('../models/user') -var nconf = require('nconf') +const passport = require('passport') +const Local = require('passport-local').Strategy +const TotpStrategy = require('passport-totp').Strategy +const JwtStrategy = require('passport-jwt').Strategy +const ExtractJwt = require('passport-jwt').ExtractJwt +const base32 = require('thirty-two') +const User = require('../models/user') +const nconf = require('nconf') module.exports = function () { passport.serializeUser(function (user, done) { @@ -48,12 +48,7 @@ module.exports = function () { return done(err) } - if (!user || user.deleted) { - req.flash('loginMessage', '') - return done(null, false, req.flash('loginMessage', 'Invalid Username/Password')) - } - - if (!User.validate(password, user.password)) { + if (!user || user.deleted || !User.validate(password, user.password)) { req.flash('loginMessage', '') return done(null, false, req.flash('loginMessage', 'Invalid Username/Password')) } @@ -100,15 +95,6 @@ module.exports = function () { if (jwtPayload.exp < Date.now() / 1000) return done({ type: 'exp' }) return done(null, jwtPayload.user) - - // User.findOne({ _id: jwtPayload.user._id }, function (err, user) { - // if (err) return done(err) - // if (user) { - // return done(null, jwtPayload.user) - // } else { - // return done(null, false) - // } - // }) } ) )