Merge pull request #20756 from overleaf/jpa-clear-admin-sessions

[web] add script for clearing admin sessions

GitOrigin-RevId: c5103b233073db62276698067b2262d7a785592b
This commit is contained in:
Jakob Ackermann 2024-10-01 21:39:48 +02:00 committed by Copybot
parent 85bc305df2
commit 39ee8de1a5
3 changed files with 87 additions and 5 deletions

View file

@ -133,7 +133,9 @@ const UserSessionsManager = {
*/ */
removeSessionsFromRedis(user, retainSessionID, callback) { removeSessionsFromRedis(user, retainSessionID, callback) {
if (!user) { if (!user) {
return callback(null) return callback(
new Error('bug: user not passed to removeSessionsFromRedis')
)
} }
const sessionSetKey = UserSessionsRedis.sessionSetKey(user) const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
rclient.smembers(sessionSetKey, function (err, sessionKeys) { rclient.smembers(sessionSetKey, function (err, sessionKeys) {
@ -155,7 +157,7 @@ const UserSessionsManager = {
{ userId: user._id }, { userId: user._id },
'no sessions in UserSessions set to delete, returning' 'no sessions in UserSessions set to delete, returning'
) )
return callback(null) return callback(null, 0)
} }
logger.debug( logger.debug(
{ userId: user._id, count: keysToDelete.length }, { userId: user._id, count: keysToDelete.length },
@ -180,7 +182,7 @@ const UserSessionsManager = {
}) })
return callback(err) return callback(err)
} }
callback(null) callback(null, keysToDelete.length)
}) })
}) })
}) })

View file

@ -0,0 +1,70 @@
const {
db,
waitForDb,
READ_PREFERENCE_SECONDARY,
} = require('../app/src/infrastructure/mongodb')
const UserSessionsManager = require('../app/src/Features/User/UserSessionsManager')
const COMMIT = process.argv.includes('--commit')
const LOG_SESSIONS = !process.argv.includes('--log-sessions=false')
async function main() {
await waitForDb()
const adminUsers = await db.users
.find(
{ isAdmin: true },
{
projection: {
_id: 1,
email: 1,
},
readPreference: READ_PREFERENCE_SECONDARY,
}
)
.toArray()
if (LOG_SESSIONS) {
for (const user of adminUsers) {
user.sessions = JSON.stringify(
await UserSessionsManager.promises.getAllUserSessions(user, [])
)
}
}
console.log('All Admin users before clearing:')
console.table(adminUsers)
if (COMMIT) {
let anyFailed = false
for (const user of adminUsers) {
console.error(
`Clearing sessions for ${user.email} (${user._id.toString()})`
)
user.clearedSessions = 0
try {
user.clearedSessions =
await UserSessionsManager.promises.removeSessionsFromRedis(user)
} catch (err) {
anyFailed = true
console.error(err)
}
}
console.log('All Admin users after clearing:')
console.table(adminUsers)
if (anyFailed) {
throw new Error('failed to clear some sessions, see above for details')
}
} else {
console.warn('Use --commit to clear sessions.')
}
}
main()
.then(() => {
console.error('Done.')
process.exit(0)
})
.catch(error => {
console.error({ error })
process.exit(1)
})

View file

@ -363,6 +363,14 @@ describe('UserSessionsManager', function () {
}) })
}) })
it('should yield the number of purged sessions', function (done) {
return this.call((err, n) => {
expect(err).to.not.exist
expect(n).to.equal(this.sessionKeys.length)
return done()
})
})
it('should call the appropriate redis methods', function (done) { it('should call the appropriate redis methods', function (done) {
return this.call(err => { return this.call(err => {
this.rclient.smembers.callCount.should.equal(1) this.rclient.smembers.callCount.should.equal(1)
@ -465,9 +473,11 @@ describe('UserSessionsManager', function () {
}) })
}) })
it('should not produce an error', function (done) { it('should produce an error', function (done) {
return this.call(err => { return this.call(err => {
expect(err).to.not.exist expect(err).to.match(
/bug: user not passed to removeSessionsFromRedis/
)
return done() return done()
}) })
}) })