Просмотр исходного кода

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

[web] add script for clearing admin sessions

GitOrigin-RevId: c5103b233073db62276698067b2262d7a785592b
Jakob Ackermann 1 год назад
Родитель
Сommit
39ee8de1a5

+ 5 - 3
services/web/app/src/Features/User/UserSessionsManager.js

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

+ 70 - 0
services/web/scripts/clear_admin_sessions.js

@@ -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)
+  })

+ 12 - 2
services/web/test/unit/src/User/UserSessionsManagerTests.js

@@ -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) {
       return this.call(err => {
         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 => {
-          expect(err).to.not.exist
+          expect(err).to.match(
+            /bug: user not passed to removeSessionsFromRedis/
+          )
           return done()
         })
       })