From e69689bb292e82f0181a00a227fe058f94029be0 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Mon, 23 Jan 2023 08:01:24 -0500 Subject: [PATCH] Merge pull request #11390 from overleaf/jpa-cleanup-sessions [web] scripts: add a script for cleaning up sessions with 2fa artifact GitOrigin-RevId: 134c2ad2c96defa54e89bbed7d7b20005c803aea --- services/web/scripts/clear_sessions_2fa.js | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 services/web/scripts/clear_sessions_2fa.js diff --git a/services/web/scripts/clear_sessions_2fa.js b/services/web/scripts/clear_sessions_2fa.js new file mode 100644 index 0000000000..73292eed7a --- /dev/null +++ b/services/web/scripts/clear_sessions_2fa.js @@ -0,0 +1,78 @@ +const { promisify, promiseMapWithLimit } = require('../app/src/util/promises') +const rClient = require('../app/src/Features/User/UserSessionsRedis').client() + +const args = require('minimist')(process.argv.slice(2)) +const CURSOR = args.cursor +const COMMIT = args.commit === 'true' +const CONCURRENCY = parseInt(args.concurrency, 10) || 50 +const LOG_EVERY_IN_S = parseInt(args['log-every-in-s'], 10) || 5 + +function shouldDelete(session) { + if (session.twoFactorAuthenticationPendingUser) { + // twoFactorAuthenticationPendingUserId migration + return true + } + + // default: keep + return false +} + +async function processSession(key) { + if (!key || !key.startsWith('sess:')) { + throw new Error(`unexpected session key: ${key}`) + } + + const blob = await rClient.get(key) + if (!blob) return false // expired or deleted + const session = JSON.parse(blob) + + if (shouldDelete(session)) { + const deleteLabel = COMMIT ? 'delete' : 'would delete' + console.warn(deleteLabel, key) + if (COMMIT) { + await rClient.del(key) + } + return true + } + return false +} + +async function main() { + console.warn({ COMMIT, CONCURRENCY, CURSOR, LOG_EVERY_IN_S }) + console.warn('starting in 10s') + await promisify(setTimeout)(10_000) + + let processed = 0 + let deleted = 0 + function logProgress() { + const deletedLabel = COMMIT ? 'deleted' : 'would have deleted' + console.log( + `processed ${processed} | ${deletedLabel} ${deleted} | cursor ${cursor}` + ) + } + + let cursor = CURSOR + let lastLog = 0 + while (cursor !== '0') { + let keys + ;[cursor, keys] = await rClient.scan(cursor || 0, 'MATCH', 'sess:*') + + const results = await promiseMapWithLimit(CONCURRENCY, keys, processSession) + processed += keys.length + for (const r of results) { + if (r) deleted++ + } + if (Date.now() - lastLog >= LOG_EVERY_IN_S * 1000) { + logProgress() + lastLog = Date.now() + } + } + logProgress() + console.log('Done.') + await rClient.disconnect() +} + +main().catch(err => { + console.error(err) + process.exit(1) +})