From 85b329af25a5513da8097fc5b5c3777787b7dd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 09:08:32 -0400 Subject: [PATCH 1/9] refactor: use array.some --- src/meta/tags.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/meta/tags.js b/src/meta/tags.js index 121c1c74c2..42e903c211 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -187,12 +187,7 @@ Tags.parse = async (req, data, meta, link) => { }; function addIfNotExists(meta, keyName, tagName, value) { - let exists = false; - meta.forEach((tag) => { - if (tag[keyName] === tagName) { - exists = true; - } - }); + const exists = meta.some(tag => tag[keyName] === tagName); if (!exists && value) { const data = { From 64875b3fae263430a2aa79f631503b98cd3031a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 11:55:24 -0400 Subject: [PATCH 2/9] feat: closes #12656, only send required meta/link tags on /api calls --- src/meta/tags.js | 117 ++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/src/meta/tags.js b/src/meta/tags.js index 42e903c211..b59760b167 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -14,8 +14,10 @@ const relative_path = nconf.get('relative_path'); const upload_url = nconf.get('upload_url'); Tags.parse = async (req, data, meta, link) => { + const isAPI = req.res && req.res.locals && req.res.locals.isAPI; + // Meta tags - const defaultTags = [{ + const defaultTags = isAPI ? [] : [{ name: 'viewport', content: 'width=device-width, initial-scale=1.0', }, { @@ -40,14 +42,14 @@ Tags.parse = async (req, data, meta, link) => { content: Meta.config.themeColor || '#ffffff', }]; - if (Meta.config.keywords) { + if (Meta.config.keywords && !isAPI) { defaultTags.push({ name: 'keywords', content: Meta.config.keywords, }); } - if (Meta.config['brand:logo']) { + if (Meta.config['brand:logo'] && !isAPI) { defaultTags.push({ name: 'msapplication-square150x150logo', content: Meta.config['brand:logo'], @@ -59,7 +61,7 @@ Tags.parse = async (req, data, meta, link) => { const cacheBuster = `${Meta.config['cache-buster'] ? `?${Meta.config['cache-buster']}` : ''}`; // Link Tags - const defaultLinks = [{ + const defaultLinks = isAPI ? [] : [{ rel: 'icon', type: 'image/x-icon', href: `${faviconPath}${cacheBuster}`, @@ -69,7 +71,7 @@ Tags.parse = async (req, data, meta, link) => { crossorigin: `use-credentials`, }]; - if (plugins.hooks.hasListeners('filter:search.query')) { + if (plugins.hooks.hasListeners('filter:search.query') && !isAPI) { defaultLinks.push({ rel: 'search', type: 'application/opensearchdescription+xml', @@ -78,7 +80,59 @@ Tags.parse = async (req, data, meta, link) => { }); } - // Touch icons for mobile-devices + if (!isAPI) { + addTouchIcons(defaultLinks); + } + + const results = await utils.promiseParallel({ + tags: plugins.hooks.fire('filter:meta.getMetaTags', { req: req, data: data, tags: defaultTags }), + links: plugins.hooks.fire('filter:meta.getLinkTags', { req: req, data: data, links: defaultLinks }), + }); + + meta = results.tags.tags.concat(meta || []).map((tag) => { + if (!tag || typeof tag.content !== 'string') { + winston.warn('Invalid meta tag. ', tag); + return tag; + } + + if (!tag.noEscape) { + const attributes = Object.keys(tag); + attributes.forEach((attr) => { + tag[attr] = utils.escapeHTML(String(tag[attr])); + }); + } + + return tag; + }); + + await addSiteOGImage(meta); + + addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); + const ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : ''); + addIfNotExists(meta, 'property', 'og:url', ogUrl); + addIfNotExists(meta, 'name', 'description', Meta.config.description); + addIfNotExists(meta, 'property', 'og:description', Meta.config.description); + + link = results.links.links.concat(link || []); + if (isAPI) { + const whitelist = ['canonical', 'alternate', 'up']; + link = link.filter(link => whitelist.some(val => val === link.rel)); + } + link = link.map((tag) => { + if (!tag.noEscape) { + const attributes = Object.keys(tag); + attributes.forEach((attr) => { + tag[attr] = utils.escapeHTML(String(tag[attr])); + }); + } + + return tag; + }); + + return { meta, link }; +}; + +function addTouchIcons(defaultLinks) { if (Meta.config['brand:touchIcon']) { defaultLinks.push({ rel: 'apple-touch-icon', @@ -142,59 +196,16 @@ Tags.parse = async (req, data, meta, link) => { href: `${relative_path}/assets/images/touch/512.png`, }); } - - const results = await utils.promiseParallel({ - tags: plugins.hooks.fire('filter:meta.getMetaTags', { req: req, data: data, tags: defaultTags }), - links: plugins.hooks.fire('filter:meta.getLinkTags', { req: req, data: data, links: defaultLinks }), - }); - - meta = results.tags.tags.concat(meta || []).map((tag) => { - if (!tag || typeof tag.content !== 'string') { - winston.warn('Invalid meta tag. ', tag); - return tag; - } - - if (!tag.noEscape) { - const attributes = Object.keys(tag); - attributes.forEach((attr) => { - tag[attr] = utils.escapeHTML(String(tag[attr])); - }); - } - - return tag; - }); - - await addSiteOGImage(meta); - - addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); - const ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : ''); - addIfNotExists(meta, 'property', 'og:url', ogUrl); - addIfNotExists(meta, 'name', 'description', Meta.config.description); - addIfNotExists(meta, 'property', 'og:description', Meta.config.description); - - link = results.links.links.concat(link || []).map((tag) => { - if (!tag.noEscape) { - const attributes = Object.keys(tag); - attributes.forEach((attr) => { - tag[attr] = utils.escapeHTML(String(tag[attr])); - }); - } - - return tag; - }); - - return { meta, link }; -}; +} function addIfNotExists(meta, keyName, tagName, value) { const exists = meta.some(tag => tag[keyName] === tagName); if (!exists && value) { - const data = { + meta.push({ content: utils.escapeHTML(String(value)), - }; - data[keyName] = tagName; - meta.push(data); + [keyName]: tagName, + }); } } From 172bc2499e91d04c7f96a09c409cc88dfd96da2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 13:09:47 -0400 Subject: [PATCH 3/9] perf: change revokeSession to work with an array of sids --- src/user/auth.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/user/auth.js b/src/user/auth.js index 954d00a0c5..fb2bca7517 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -107,26 +107,28 @@ module.exports = function (User) { db.sortedSetAdd(`uid:${uid}:sessions`, Date.now(), sessionId), db.setObjectField(`uid:${uid}:sessionUUID:sessionId`, uuid, sessionId), ]); - await revokeSessionsAboveThreshold(uid, meta.config.maxUserSessions); + await revokeSessionsAboveThreshold(uid); }; - async function revokeSessionsAboveThreshold(uid, maxUserSessions) { + async function revokeSessionsAboveThreshold(uid) { const activeSessions = await db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1); - if (activeSessions.length > maxUserSessions) { - const sessionsToRevoke = activeSessions.slice(0, activeSessions.length - maxUserSessions); - await Promise.all(sessionsToRevoke.map(sessionId => User.auth.revokeSession(sessionId, uid))); + if (activeSessions.length > meta.config.maxUserSessions) { + const sessionsToRevoke = activeSessions.slice(0, activeSessions.length - meta.config.maxUserSessions); + await User.auth.revokeSession(sessionsToRevoke, uid); } } - User.auth.revokeSession = async function (sessionId, uid) { - winston.verbose(`[user.auth] Revoking session ${sessionId} for user ${uid}`); - const sessionObj = await db.sessionStoreGet(sessionId); - if (sessionObj && sessionObj.meta && sessionObj.meta.uuid) { - await db.deleteObjectField(`uid:${uid}:sessionUUID:sessionId`, sessionObj.meta.uuid); - } + User.auth.revokeSession = async function (sessionIds, uid) { + sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]; + const sessionObjs = await Promise.all(sessionIds.map(db.sessionStoreGet)); + const sidsToDestroy = sessionObjs.filter(Boolean).map((s, i) => sessionIds[i]); + const uuidsToDelete = sessionObjs.filter(s => s && s.meta && s.meta.uuid).map(s => s.meta.uuid); + const destroySids = sids => Promise.all(sids.map(db.sessionStoreDestroy)); + await Promise.all([ - db.sortedSetRemove(`uid:${uid}:sessions`, sessionId), - db.sessionStoreDestroy(sessionId), + db.deleteObjectFields(`uid:${uid}:sessionUUID:sessionId`, uuidsToDelete), + db.sortedSetRemove(`uid:${uid}:sessions`, sessionIds), + destroySids(sidsToDestroy), ]); }; @@ -137,7 +139,7 @@ module.exports = function (User) { uids.forEach((uid, index) => { const ids = sids[index].filter(id => id !== except); if (ids.length) { - promises.push(ids.map(s => User.auth.revokeSession(s, uid))); + promises.push(User.auth.revokeSession(ids, uid)); } }); await Promise.all(promises); From 69ce3bf0b8d7a35e6449ff1ac5abe2addde5a3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 13:25:52 -0400 Subject: [PATCH 4/9] lint: remove unused winston --- src/user/auth.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/user/auth.js b/src/user/auth.js index fb2bca7517..0ce177f183 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -1,6 +1,5 @@ 'use strict'; -const winston = require('winston'); const validator = require('validator'); const _ = require('lodash'); const db = require('../database'); From 26feb2bbf809bcbbeec63e9300225f7ab387b488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 14:16:41 -0400 Subject: [PATCH 5/9] perf: cleanup sessions every 30 seconds instead of everytime addSession is called --- src/user/auth.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/user/auth.js b/src/user/auth.js index 0ce177f183..da1e0c80ed 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -2,6 +2,8 @@ const validator = require('validator'); const _ = require('lodash'); +const winston = require('winston'); +const cronJob = require('cron').CronJob; const db = require('../database'); const meta = require('../meta'); const events = require('../events'); @@ -11,6 +13,21 @@ const utils = require('../utils'); module.exports = function (User) { User.auth = {}; + const uidsToClean = Object.create(null); + + new cronJob('*/30 * * * * *', (async () => { + const uids = Object.keys(uidsToClean); + try { + await Promise.all(uids.map(async (uid) => { + await cleanExpiredSessions(uid); + await revokeSessionsAboveThreshold(uid); + delete uidsToClean[uid]; + })); + } catch (err) { + winston.error(err.stack); + } + }), null, true); + User.auth.logAttempt = async function (uid, ip) { if (!(parseInt(uid, 10) > 0)) { return; @@ -101,12 +118,12 @@ module.exports = function (User) { if (!(parseInt(uid, 10) > 0)) { return; } - await cleanExpiredSessions(uid); + const now = Date.now(); + uidsToClean[uid] = now; await Promise.all([ - db.sortedSetAdd(`uid:${uid}:sessions`, Date.now(), sessionId), + db.sortedSetAdd(`uid:${uid}:sessions`, now, sessionId), db.setObjectField(`uid:${uid}:sessionUUID:sessionId`, uuid, sessionId), ]); - await revokeSessionsAboveThreshold(uid); }; async function revokeSessionsAboveThreshold(uid) { From 65a91ea5e5e3beb36df17c456bc50cf67c24474b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 14:32:37 -0400 Subject: [PATCH 6/9] refactor: move delete call --- src/user/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/auth.js b/src/user/auth.js index da1e0c80ed..954cba54c6 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -19,9 +19,9 @@ module.exports = function (User) { const uids = Object.keys(uidsToClean); try { await Promise.all(uids.map(async (uid) => { + delete uidsToClean[uid]; await cleanExpiredSessions(uid); await revokeSessionsAboveThreshold(uid); - delete uidsToClean[uid]; })); } catch (err) { winston.error(err.stack); From 9108c900c6910536e9926e440f0f7c7709023fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 15:30:54 -0400 Subject: [PATCH 7/9] test: move set --- src/user/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/auth.js b/src/user/auth.js index 954cba54c6..b35b7d6323 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -119,11 +119,11 @@ module.exports = function (User) { return; } const now = Date.now(); - uidsToClean[uid] = now; await Promise.all([ db.sortedSetAdd(`uid:${uid}:sessions`, now, sessionId), db.setObjectField(`uid:${uid}:sessionUUID:sessionId`, uuid, sessionId), ]); + uidsToClean[uid] = now; }; async function revokeSessionsAboveThreshold(uid) { From d6c946cf145ae01120a4a86ba1572e723dbfeb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Jun 2024 17:34:04 -0400 Subject: [PATCH 8/9] refactor: sessionUUID (#12658) * refactor: sessionUUID * test: get uuid from sessionsstore * refactor: dont load all sids again in revoke * feat: upgrade script, get rid of second sessionStoreGet * allow disabling maxUserSessions by setting to 0 --- src/controllers/authentication.js | 2 +- src/upgrades/3.8.3/remove-session-uuid.js | 21 ++++++++ src/user/auth.js | 63 +++++++---------------- src/user/delete.js | 2 +- test/api.js | 6 ++- test/authentication.js | 2 +- 6 files changed, 48 insertions(+), 48 deletions(-) create mode 100644 src/upgrades/3.8.3/remove-session-uuid.js diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index b3949905d4..bbffc9070a 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -380,7 +380,7 @@ authenticationController.onSuccessfulLogin = async function (req, uid) { new Promise((resolve) => { req.session.save(resolve); }), - user.auth.addSession(uid, req.sessionID, uuid), + user.auth.addSession(uid, req.sessionID), user.updateLastOnlineTime(uid), user.onUserOnline(uid, Date.now()), analytics.increment('logins'), diff --git a/src/upgrades/3.8.3/remove-session-uuid.js b/src/upgrades/3.8.3/remove-session-uuid.js new file mode 100644 index 0000000000..59a975fce2 --- /dev/null +++ b/src/upgrades/3.8.3/remove-session-uuid.js @@ -0,0 +1,21 @@ +'use strict'; + + +const db = require('../../database'); +const batch = require('../../batch'); + +module.exports = { + name: 'Remove uid::sessionUUID:sessionId object', + timestamp: Date.UTC(2024, 5, 26), + method: async function () { + const { progress } = this; + + await batch.processSortedSet('users:joindate', async (uids) => { + progress.incr(uids.length); + await db.deleteAll(uids.map(uid => `uid:${uid}:sessionUUID:sessionId`)); + }, { + batch: 500, + progress: progress, + }); + }, +}; diff --git a/src/user/auth.js b/src/user/auth.js index b35b7d6323..0adf589967 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -2,8 +2,6 @@ const validator = require('validator'); const _ = require('lodash'); -const winston = require('winston'); -const cronJob = require('cron').CronJob; const db = require('../database'); const meta = require('../meta'); const events = require('../events'); @@ -13,21 +11,6 @@ const utils = require('../utils'); module.exports = function (User) { User.auth = {}; - const uidsToClean = Object.create(null); - - new cronJob('*/30 * * * * *', (async () => { - const uids = Object.keys(uidsToClean); - try { - await Promise.all(uids.map(async (uid) => { - delete uidsToClean[uid]; - await cleanExpiredSessions(uid); - await revokeSessionsAboveThreshold(uid); - })); - } catch (err) { - winston.error(err.stack); - } - }), null, true); - User.auth.logAttempt = async function (uid, ip) { if (!(parseInt(uid, 10) > 0)) { return; @@ -93,58 +76,53 @@ module.exports = function (User) { }; async function cleanExpiredSessions(uid) { - const uuidMapping = await db.getObject(`uid:${uid}:sessionUUID:sessionId`); - if (!uuidMapping) { - return; + const sids = await db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1); + if (!sids.length) { + return []; } - const expiredUUIDs = []; + const expiredSids = []; - await Promise.all(Object.keys(uuidMapping).map(async (uuid) => { - const sid = uuidMapping[uuid]; + const activeSids = []; + await Promise.all(sids.map(async (sid) => { const sessionObj = await db.sessionStoreGet(sid); const expired = !sessionObj || !sessionObj.hasOwnProperty('passport') || !sessionObj.passport.hasOwnProperty('user') || parseInt(sessionObj.passport.user, 10) !== parseInt(uid, 10); if (expired) { - expiredUUIDs.push(uuid); expiredSids.push(sid); + } else { + activeSids.push(sid); } })); - await db.deleteObjectFields(`uid:${uid}:sessionUUID:sessionId`, expiredUUIDs); + await db.sortedSetRemove(`uid:${uid}:sessions`, expiredSids); + return activeSids; } - User.auth.addSession = async function (uid, sessionId, uuid) { + User.auth.addSession = async function (uid, sessionId) { if (!(parseInt(uid, 10) > 0)) { return; } - const now = Date.now(); - await Promise.all([ - db.sortedSetAdd(`uid:${uid}:sessions`, now, sessionId), - db.setObjectField(`uid:${uid}:sessionUUID:sessionId`, uuid, sessionId), - ]); - uidsToClean[uid] = now; + + const activeSids = await cleanExpiredSessions(uid); + await db.sortedSetAdd(`uid:${uid}:sessions`, Date.now(), sessionId); + await revokeSessionsAboveThreshold(activeSids.push(sessionId), uid); }; - async function revokeSessionsAboveThreshold(uid) { - const activeSessions = await db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1); - if (activeSessions.length > meta.config.maxUserSessions) { - const sessionsToRevoke = activeSessions.slice(0, activeSessions.length - meta.config.maxUserSessions); + async function revokeSessionsAboveThreshold(activeSids, uid) { + if (meta.config.maxUserSessions > 0 && activeSids.length > meta.config.maxUserSessions) { + const sessionsToRevoke = activeSids.slice(0, activeSids.length - meta.config.maxUserSessions); await User.auth.revokeSession(sessionsToRevoke, uid); } } User.auth.revokeSession = async function (sessionIds, uid) { sessionIds = Array.isArray(sessionIds) ? sessionIds : [sessionIds]; - const sessionObjs = await Promise.all(sessionIds.map(db.sessionStoreGet)); - const sidsToDestroy = sessionObjs.filter(Boolean).map((s, i) => sessionIds[i]); - const uuidsToDelete = sessionObjs.filter(s => s && s.meta && s.meta.uuid).map(s => s.meta.uuid); const destroySids = sids => Promise.all(sids.map(db.sessionStoreDestroy)); await Promise.all([ - db.deleteObjectFields(`uid:${uid}:sessionUUID:sessionId`, uuidsToDelete), db.sortedSetRemove(`uid:${uid}:sessions`, sessionIds), - destroySids(sidsToDestroy), + destroySids(sessionIds), ]); }; @@ -164,11 +142,10 @@ module.exports = function (User) { User.auth.deleteAllSessions = async function () { await batch.processSortedSet('users:joindate', async (uids) => { const sessionKeys = uids.map(uid => `uid:${uid}:sessions`); - const sessionUUIDKeys = uids.map(uid => `uid:${uid}:sessionUUID:sessionId`); const sids = _.flatten(await db.getSortedSetRange(sessionKeys, 0, -1)); await Promise.all([ - db.deleteAll(sessionKeys.concat(sessionUUIDKeys)), + db.deleteAll(sessionKeys), ...sids.map(sid => db.sessionStoreDestroy(sid)), ]); }, { batch: 1000 }); diff --git a/src/user/delete.js b/src/user/delete.js index b84b4ef1d8..8f99117c59 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -119,7 +119,7 @@ module.exports = function (User) { `uid:${uid}:chat:rooms:read`, `uid:${uid}:upvote`, `uid:${uid}:downvote`, `uid:${uid}:flag:pids`, - `uid:${uid}:sessions`, `uid:${uid}:sessionUUID:sessionId`, + `uid:${uid}:sessions`, `invitation:uid:${uid}`, ]; diff --git a/test/api.js b/test/api.js index 47961742ff..0ea9918953 100644 --- a/test/api.js +++ b/test/api.js @@ -562,8 +562,10 @@ describe('API', async () => { const reloginPaths = ['GET /api/user/{userslug}/edit/email', 'PUT /users/{uid}/password', 'DELETE /users/{uid}/sessions/{uuid}']; if (reloginPaths.includes(`${method.toUpperCase()} ${path}`)) { ({ jar } = await helpers.loginUser('admin', '123456')); - const sessionUUIDs = await db.getObject('uid:1:sessionUUID:sessionId'); - mocks.delete['/users/{uid}/sessions/{uuid}'][1].example = Object.keys(sessionUUIDs).pop(); + const sessionIds = await db.getSortedSetRange('uid:1:sessions', 0, -1); + const sessObj = await db.sessionStoreGet(sessionIds[0]); + const { uuid } = sessObj.meta; + mocks.delete['/users/{uid}/sessions/{uuid}'][1].example = uuid; // Retrieve CSRF token using cookie, to test Write API csrfToken = await helpers.getCsrfToken(jar); diff --git a/test/authentication.js b/test/authentication.js index 1dcbe176a8..193d617435 100644 --- a/test/authentication.js +++ b/test/authentication.js @@ -195,7 +195,7 @@ describe('authentication', () => { }); assert(body); assert.equal(body.username, username); - const sessions = await db.getObject(`uid:${uid}:sessionUUID:sessionId`); + const sessions = await db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1); assert(sessions); assert(Object.keys(sessions).length > 0); }); From 61e5293a76aafe9e09f3c665ac9f514a94b4769d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 27 Jun 2024 10:26:06 -0400 Subject: [PATCH 9/9] test: dont track session for api/v3 (#12660) since they get destroyed when the request ends --- src/controllers/authentication.js | 4 ++-- src/middleware/user.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index bbffc9070a..6591459cf2 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -342,7 +342,7 @@ authenticationController.doLogin = async function (req, uid) { await authenticationController.onSuccessfulLogin(req, uid); }; -authenticationController.onSuccessfulLogin = async function (req, uid) { +authenticationController.onSuccessfulLogin = async function (req, uid, trackSession = true) { /* * Older code required that this method be called from within the SSO plugin. * That behaviour is no longer required, onSuccessfulLogin is now automatically @@ -380,7 +380,7 @@ authenticationController.onSuccessfulLogin = async function (req, uid) { new Promise((resolve) => { req.session.save(resolve); }), - user.auth.addSession(uid, req.sessionID), + trackSession ? user.auth.addSession(uid, req.sessionID) : undefined, user.updateLastOnlineTime(uid), user.onUserOnline(uid, Date.now()), analytics.increment('logins'), diff --git a/src/middleware/user.js b/src/middleware/user.js index 3b7c1168db..ca6afcaf9b 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -41,7 +41,7 @@ module.exports = function (middleware) { async function finishLogin(req, user) { const loginAsync = util.promisify(req.login).bind(req); await loginAsync(user, { keepSessionInfo: true }); - await controllers.authentication.onSuccessfulLogin(req, user.uid); + await controllers.authentication.onSuccessfulLogin(req, user.uid, false); req.uid = parseInt(user.uid, 10); req.loggedIn = req.uid > 0; return true;