From f98a1cc5403fa3618231b2abdab32f8f7d0ada5f Mon Sep 17 00:00:00 2001 From: vishnu Date: Wed, 3 Apr 2024 14:25:28 +0530 Subject: [PATCH 1/4] 1310 fix sending correct error message --- src/locales/en.json | 3 ++- src/middlewares/authenticator.js | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 7eda9ffad..c43bdb5fb 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -123,5 +123,6 @@ "USER_SESSION_FETCHED_SUCCESSFULLY": "User sessions fetched successfully", "USER_SESSIONS_REMOVED_SUCCESSFULLY": "User sesions removed successfully", "PASSWORD_CHANGED_SUCCESSFULLY": "Your password has been changed successfully. Please log-in to continue.", - "ACTIVE_SESSION_LIMIT_EXCEEDED": "Sorry! Your allowed active session limi exceeded. Please log-off from other sessions to continue" + "ACTIVE_SESSION_LIMIT_EXCEEDED": "Sorry! Your allowed active session limi exceeded. Please log-off from other sessions to continue", + "USER_SESSION_NOT_FOUND": "User session not found" } diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index c80443f22..0515ca050 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -124,7 +124,11 @@ module.exports = async function (req, res, next) { // If data is not in redis, token is invalid if (!redisData || redisData.accessToken !== authHeaderArray[1]) { - throw unAuthorizedResponse + throw responses.failureResponse({ + message: 'USER_SESSION_NOT_FOUND', + statusCode: httpStatusCode.unauthorized, + responseCode: 'UNAUTHORIZED', + }) } // Renew the TTL if allowed idle time is greater than zero @@ -132,7 +136,7 @@ module.exports = async function (req, res, next) { await utilsHelper.redisSet(sessionId, redisData, process.env.ALLOWED_IDLE_TIME) } } catch (err) { - if (err.name === 'TokenExpiredError') { + if (err.name === 'TokenExpiredError' || err.message === 'USER_SESSION_NOT_FOUND') { throw responses.failureResponse({ message: 'ACCESS_TOKEN_EXPIRED', statusCode: httpStatusCode.unauthorized, @@ -140,7 +144,6 @@ module.exports = async function (req, res, next) { }) } else throw unAuthorizedResponse } - if (!decodedToken) throw unAuthorizedResponse //check for admin user From ddd4c1f8786d6043629460465a455872e982115d Mon Sep 17 00:00:00 2001 From: adithya_dinesh Date: Wed, 3 Apr 2024 19:31:18 +0530 Subject: [PATCH 2/4] Captcha and otp security changes --- src/constants/common.js | 2 +- src/services/account.js | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/constants/common.js b/src/constants/common.js index 5170a5d68..29d1935cd 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -91,5 +91,5 @@ module.exports = { DELETED_STATUS: 'DELETED', DEFAULT_ORG_VISIBILITY: 'PUBLIC', ROLE_TYPE_NON_SYSTEM: 0, - captchaEnabledAPIs: ['/user/v1/account/login', '/user/v1/account/create', '/user/v1/account/resetPassword'], + captchaEnabledAPIs: ['/user/v1/account/login', '/user/v1/account/generateOtp', '/user/v1/account/registrationOtp'], } diff --git a/src/services/account.js b/src/services/account.js index ef1d6b84b..4d1de6a92 100644 --- a/src/services/account.js +++ b/src/services/account.js @@ -838,6 +838,15 @@ module.exports = class AccountHelper { try { const plaintextEmailId = bodyData.email.toLowerCase() const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) + + const redisData = await utilsHelper.redisGet(encryptedEmailId) + if (!redisData || redisData.otp != bodyData.otp) { + return responses.failureResponse({ + message: 'RESET_OTP_INVALID', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } const userCredentials = await UserCredentialQueries.findOne({ email: encryptedEmailId, password: { @@ -876,14 +885,6 @@ module.exports = class AccountHelper { } user.user_roles = roles - const redisData = await utilsHelper.redisGet(encryptedEmailId) - if (!redisData || redisData.otp != bodyData.otp) { - return responses.failureResponse({ - message: 'RESET_OTP_INVALID', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', - }) - } const isPasswordSame = bcryptJs.compareSync(bodyData.password, userCredentials.password) if (isPasswordSame) { return responses.failureResponse({ From 67126702ebdafd187c6c9749bd854009401e3c20 Mon Sep 17 00:00:00 2001 From: vishnu Date: Thu, 4 Apr 2024 09:25:35 +0530 Subject: [PATCH 3/4] changes added to keep current session on top of first page --- src/controllers/v1/account.js | 3 ++- src/services/user-sessions.js | 23 +++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/controllers/v1/account.js b/src/controllers/v1/account.js index 071aca529..7382ea30e 100644 --- a/src/controllers/v1/account.js +++ b/src/controllers/v1/account.js @@ -284,7 +284,8 @@ module.exports = class Account { req.decodedToken.id, filter, req.pageSize, - req.pageNo + req.pageNo, + req.decodedToken.session_id ) return userSessionDetails } catch (error) { diff --git a/src/services/user-sessions.js b/src/services/user-sessions.js index 64b60a058..ad9a2bebc 100644 --- a/src/services/user-sessions.js +++ b/src/services/user-sessions.js @@ -84,10 +84,11 @@ module.exports = class UserSessionsHelper { * @param {string} status - The status of the user sessions (e.g., 'ACTIVE', ''). * @param {number} limit - The maximum number of user sessions to retrieve per page. * @param {number} page - The page number for pagination. + * @param {number} currentSessionId - The id of current session. * @returns {Promise} - A promise that resolves to the user session details. */ - static async list(userId, status, limit, page) { + static async list(userId, status, limit, page, currentSessionId) { try { const filter = { user_id: userId, @@ -103,6 +104,7 @@ module.exports = class UserSessionsHelper { const userSessions = await userSessionsQueries.findAll(filter) const activeSessions = [] const inActiveSessions = [] + const currentSession = [] for (const session of userSessions) { const id = session.id.toString() // Convert ID to string const redisData = await utilsHelper.redisGet(id) @@ -127,7 +129,11 @@ module.exports = class UserSessionsHelper { login_time: session.started_at, logout_time: session.ended_at, } - activeSessions.push(responseObj) + if (responseObj.id == currentSessionId) { + currentSession.push(responseObj) + } else { + activeSessions.push(responseObj) + } } else if (status === '') { const responseObj = { id: session.id, @@ -136,13 +142,18 @@ module.exports = class UserSessionsHelper { login_time: session.started_at, logout_time: session.ended_at, } - responseObj.status === common.ACTIVE_STATUS - ? activeSessions.push(responseObj) - : inActiveSessions.push(responseObj) + // get current session data + if (responseObj.id == currentSessionId) { + currentSession.push(responseObj) + } else { + responseObj.status === common.ACTIVE_STATUS + ? activeSessions.push(responseObj) + : inActiveSessions.push(responseObj) + } } } - const result = [...activeSessions, ...inActiveSessions] + const result = [...currentSession, ...activeSessions, ...inActiveSessions] // Paginate the result array // The response is accumulated from two places. db and redis. So pagination is not possible on the fly From ef218ce7e1521b0512c3483d5db8cb39ecc4a8d4 Mon Sep 17 00:00:00 2001 From: vishnu Date: Thu, 4 Apr 2024 10:50:32 +0530 Subject: [PATCH 4/4] add period filter --- src/controllers/v1/account.js | 3 ++- src/services/user-sessions.js | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/controllers/v1/account.js b/src/controllers/v1/account.js index 7382ea30e..b49cdc363 100644 --- a/src/controllers/v1/account.js +++ b/src/controllers/v1/account.js @@ -285,7 +285,8 @@ module.exports = class Account { filter, req.pageSize, req.pageNo, - req.decodedToken.session_id + req.decodedToken.session_id, + req.query && req.query.period ? req.query.period : '' ) return userSessionDetails } catch (error) { diff --git a/src/services/user-sessions.js b/src/services/user-sessions.js index ad9a2bebc..7f00468fd 100644 --- a/src/services/user-sessions.js +++ b/src/services/user-sessions.js @@ -12,6 +12,8 @@ const httpStatusCode = require('@generics/http-status') const responses = require('@helpers/responses') const common = require('@constants/common') const jwt = require('jsonwebtoken') +const moment = require('moment') +const { Op } = require('sequelize') // create user-session module.exports = class UserSessionsHelper { @@ -88,7 +90,7 @@ module.exports = class UserSessionsHelper { * @returns {Promise} - A promise that resolves to the user session details. */ - static async list(userId, status, limit, page, currentSessionId) { + static async list(userId, status, limit, page, currentSessionId, period = '') { try { const filter = { user_id: userId, @@ -100,6 +102,14 @@ module.exports = class UserSessionsHelper { filter.ended_at = null } + // If front end passes a period + if (period != '') { + const periodInSeconds = await utilsHelper.convertDurationToSeconds(period) + const currentTimeEpoch = moment().unix() + const threshold = currentTimeEpoch - periodInSeconds + filter.started_at = { [Op.gte]: threshold } + } + // create userSession const userSessions = await userSessionsQueries.findAll(filter) const activeSessions = []