diff --git a/public/src/admin/settings/cookies.js b/public/src/admin/settings/cookies.js index d113d99dba..0e85691c12 100644 --- a/public/src/admin/settings/cookies.js +++ b/public/src/admin/settings/cookies.js @@ -9,6 +9,16 @@ define('admin/settings/cookies', [ Module.init = function () { colorpicker.enable($('[data-colorpicker="1"]')); + + $('#delete-all-sessions').on('click', function () { + socket.emit('admin.deleteAllSessions', function (err) { + if (err) { + return app.alertError(err.message); + } + window.location.href = config.relative_path + '/login'; + }); + return false; + }); }; return Module; diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index d9312cc871..88b800a5ce 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -3,7 +3,6 @@ var async = require('async'); var winston = require('winston'); var nconf = require('nconf'); -var path = require('path'); var meta = require('../meta'); var plugins = require('../plugins'); @@ -285,5 +284,9 @@ SocketAdmin.getSearchDict = function (socket, data, callback) { }); }; +SocketAdmin.deleteAllSessions = function (socket, data, callback) { + user.auth.deleteAllSessions(callback); +}; + module.exports = SocketAdmin; diff --git a/src/user/auth.js b/src/user/auth.js index a6222728e4..219c468304 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -5,6 +5,7 @@ var winston = require('winston'); var db = require('../database'); var meta = require('../meta'); var events = require('../events'); +var batch = require('../batch'); module.exports = function (User) { User.auth = {}; @@ -142,4 +143,36 @@ module.exports = function (User) { } ], callback); }; + + User.auth.deleteAllSessions = function (callback) { + var _ = require('underscore'); + batch.processSortedSet('users:joindate', function (uids, next) { + + var sessionKeys = uids.map(function (uid) { + return 'uid:' + uid + ':sessions'; + }); + + var sessionUUIDKeys = uids.map(function (uid) { + return 'uid:' + uid + ':sessionUUID:sessionId'; + }); + + async.waterfall([ + function (next) { + db.getSortedSetRange(sessionKeys, 0, -1, next); + }, + function (sids, next) { + sids = _.flatten(sids); + async.parallel([ + async.apply(db.deleteAll, sessionUUIDKeys), + async.apply(db.deleteAll, sessionKeys), + function (next) { + async.each(sids, function (sid, next) { + db.sessionStore.destroy(sid, next); + }, next); + } + ], next); + } + ], next); + }, {batch: 1000}, callback); + }; }; \ No newline at end of file diff --git a/src/views/admin/settings/cookies.tpl b/src/views/admin/settings/cookies.tpl index 8717d63729..516aa8ceca 100644 --- a/src/views/admin/settings/cookies.tpl +++ b/src/views/admin/settings/cookies.tpl @@ -48,6 +48,13 @@ Leave blank for default

+ +
+ +

+ This will delete all sessions, you will be logged out and will have to login again! +

+
diff --git a/test/authentication.js b/test/authentication.js index 38860e873a..e3ab49a5f5 100644 --- a/test/authentication.js +++ b/test/authentication.js @@ -10,10 +10,11 @@ var user = require('../src/user'); describe('authentication', function () { var jar = request.jar(); - + var regularUid; before(function (done) { - user.create({username: 'regular', password: 'regularpwd', email: 'regular@nodebb.org' }, function (err) { + user.create({username: 'regular', password: 'regularpwd', email: 'regular@nodebb.org' }, function (err, uid) { assert.ifError(err); + regularUid = uid; done(); }); }); @@ -71,7 +72,7 @@ describe('authentication', function () { headers: { 'x-csrf-token': body.csrf_token } - }, function (err, response, body) { + }, function (err) { assert.ifError(err); request({ @@ -125,6 +126,23 @@ describe('authentication', function () { }); }); + it('should revoke all sessions', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + db.sortedSetCard('uid:' + regularUid + ':sessions', function (err, count) { + assert.ifError(err); + assert(count); + socketAdmin.deleteAllSessions({uid: 1}, {}, function (err) { + assert.ifError(err); + db.sortedSetCard('uid:' + regularUid + ':sessions', function (err, count) { + assert.ifError(err); + assert(!count); + done(); + }); + }); + }); + + }); + after(function (done) { db.emptydb(done);