diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 31fa107cfb..fb6d6e31ec 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -60,7 +60,6 @@ User.createUser = function (socket, userData, callback) { user.create(userData, callback); }; - User.resetLockouts = function (socket, uids, callback) { if (!Array.isArray(uids)) { return callback(new Error('[[error:invalid-data]]')); @@ -185,25 +184,25 @@ function deleteUsers(socket, uids, method, callback) { } User.search = function (socket, data, callback) { - user.search({query: data.query, searchBy: data.searchBy, uid: socket.uid}, function (err, searchData) { - if (err) { - return callback(err); - } - if (!searchData.users.length) { - return callback(null, searchData); - } - - var userData = searchData.users; - var uids = userData.map(function (user) { - return user && user.uid; - }); - - user.getUsersFields(uids, ['email', 'flags', 'lastonline', 'joindate'], function (err, userInfo) { - if (err) { - return callback(err); + var searchData; + async.waterfall([ + function (next) { + user.search({query: data.query, searchBy: data.searchBy, uid: socket.uid}, next); + }, + function (_searchData, next) { + searchData = _searchData; + if (!searchData.users.length) { + return callback(null, searchData); } - userData.forEach(function (user, index) { + var uids = searchData.users.map(function (user) { + return user && user.uid; + }); + + user.getUsersFields(uids, ['email', 'flags', 'lastonline', 'joindate'], next); + }, + function (userInfo, next) { + searchData.users.forEach(function (user, index) { if (user && userInfo[index]) { user.email = validator.escape(String(userInfo[index].email || '')); user.flags = userInfo[index].flags || 0; @@ -211,10 +210,9 @@ User.search = function (socket, data, callback) { user.joindateISO = userInfo[index].joindateISO; } }); - - callback(null, searchData); - }); - }); + next(null, searchData); + } + ], callback); }; User.deleteInvitation = function (socket, data, callback) { diff --git a/src/user/auth.js b/src/user/auth.js index a60f59fea4..a6222728e4 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -10,43 +10,37 @@ module.exports = function (User) { User.auth = {}; User.auth.logAttempt = function (uid, ip, callback) { - db.exists('lockout:' + uid, function (err, exists) { - if (err) { - return callback(err); - } - - if (exists) { - return callback(new Error('[[error:account-locked]]')); - } - - db.increment('loginAttempts:' + uid, function (err, attempts) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + db.exists('lockout:' + uid, next); + }, + function (exists, next) { + if (exists) { + return callback(new Error('[[error:account-locked]]')); } - - if ((meta.config.loginAttempts || 5) < attempts) { - // Lock out the account - db.set('lockout:' + uid, '', function (err) { - if (err) { - return callback(err); - } - var duration = 1000 * 60 * (meta.config.lockoutDuration || 60); - - db.delete('loginAttempts:' + uid); - db.pexpire('lockout:' + uid, duration); - events.log({ - type: 'account-locked', - uid: uid, - ip: ip - }); - callback(new Error('[[error:account-locked]]')); - }); - } else { - db.pexpire('loginAttempts:' + uid, 1000 * 60 * 60); - callback(); + db.increment('loginAttempts:' + uid, next); + }, + function (attemps, next) { + var loginAttempts = parseInt(meta.config.loginAttempts, 10) || 5; + if (attemps <= loginAttempts) { + return db.pexpire('loginAttempts:' + uid, 1000 * 60 * 60, callback); } - }); - }); + // Lock out the account + db.set('lockout:' + uid, '', next); + }, + function (next) { + var duration = 1000 * 60 * (meta.config.lockoutDuration || 60); + + db.delete('loginAttempts:' + uid); + db.pexpire('lockout:' + uid, duration); + events.log({ + type: 'account-locked', + uid: uid, + ip: ip + }); + next(new Error('[[error:account-locked]]')); + } + ], callback); }; User.auth.clearLoginAttempts = function (uid) { diff --git a/test/socket.io.js b/test/socket.io.js index 95fc8d911b..345d8782f4 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -30,7 +30,7 @@ describe('socket.io', function () { before(function (done) { async.series([ async.apply(user.create, { username: 'admin', password: 'adminpwd' }), - async.apply(user.create, { username: 'regular', password: 'regularpwd' }), + async.apply(user.create, { username: 'regular', password: 'regularpwd', email: 'regular@test.com'}), async.apply(categories.create, { name: 'Test Category', description: 'Test category created by testing script' @@ -42,7 +42,7 @@ describe('socket.io', function () { adminUid = data[0]; regularUid = data[1]; cid = data[2].cid; - + groups.resetCache(); groups.join('administrators', data[0], done); }); }); @@ -171,6 +171,110 @@ describe('socket.io', function () { }); }); + it('should make user admin', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.makeAdmins({uid: adminUid}, [regularUid], function (err) { + assert.ifError(err); + groups.isMember(regularUid, 'administrators', function (err, isMember) { + assert.ifError(err); + assert(isMember); + done(); + }); + }); + }); + + it('should make user non-admin', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.removeAdmins({uid: adminUid}, [regularUid], function (err) { + assert.ifError(err); + groups.isMember(regularUid, 'administrators', function (err, isMember) { + assert.ifError(err); + assert(!isMember); + done(); + }); + }); + }); + + describe('create/delete', function () { + var socketAdmin = require('../src/socket.io/admin'); + var uid; + it('should create a user', function (done) { + socketAdmin.user.createUser({uid: adminUid}, {username: 'foo1'}, function (err, _uid) { + assert.ifError(err); + uid = _uid; + groups.isMember(uid, 'registered-users', function (err, isMember) { + assert.ifError(err); + assert(isMember); + done(); + }); + }); + }); + + it('should delete users', function (done) { + socketAdmin.user.deleteUsers({uid: adminUid}, [uid], function (err) { + assert.ifError(err); + groups.isMember(uid, 'registered-users', function (err, isMember) { + assert.ifError(err); + assert(!isMember); + done(); + }); + }); + }); + + it('should delete users and their content', function (done) { + socketAdmin.user.deleteUsersAndContent({uid: adminUid}, [uid], function (err) { + assert.ifError(err); + done(); + }); + }); + }); + + it('should error with invalid data', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.createUser({uid: adminUid}, null, function (err) { + assert.equal(err.message, '[[error:invalid-data]]'); + done(); + }); + }); + + it('should reset lockouts', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.resetLockouts({uid: adminUid}, [regularUid], function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should reset flags', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.resetFlags({uid: adminUid}, [regularUid], function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should validate emails', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.validateEmail({uid: adminUid}, [regularUid], function (err) { + assert.ifError(err); + user.getUserField(regularUid, 'email:confirmed', function (err, emailConfirmed) { + assert.ifError(err); + assert.equal(parseInt(emailConfirmed, 10), 1); + done(); + }); + }); + }); + + it('should search users', function (done) { + var socketAdmin = require('../src/socket.io/admin'); + socketAdmin.user.search({uid: adminUid}, {query: 'reg', searchBy: 'username'}, function (err, data) { + assert.ifError(err); + assert.equal(data.matchCount, 1); + assert.equal(data.users[0].username, 'regular'); + done(); + }); + }); + after(function (done) { db.emptydb(done); });