mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
more auth tests
This commit is contained in:
@@ -52,19 +52,7 @@ authenticationController.register = function (req, res) {
|
||||
user.isPasswordValid(userData.password, next);
|
||||
},
|
||||
function (next) {
|
||||
if (registrationType === 'normal' || registrationType === 'invite-only' || registrationType === 'admin-invite-only') {
|
||||
next(null, false);
|
||||
} else if (registrationType === 'admin-approval') {
|
||||
next(null, true);
|
||||
} else if (registrationType === 'admin-approval-ip') {
|
||||
db.sortedSetCard('ip:' + req.ip + ':uid', function (err, count) {
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
next(null, !!count);
|
||||
}
|
||||
});
|
||||
}
|
||||
user.shouldQueueUser(req.ip, next);
|
||||
},
|
||||
function (queue, next) {
|
||||
res.locals.processLogin = true; // set it to false in plugin if you wish to just register only
|
||||
@@ -200,13 +188,15 @@ authenticationController.login = function (req, res, next) {
|
||||
var loginWith = meta.config.allowLoginWith || 'username-email';
|
||||
|
||||
if (req.body.username && utils.isEmailValid(req.body.username) && loginWith.indexOf('email') !== -1) {
|
||||
user.getUsernameByEmail(req.body.username, function (err, username) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
req.body.username = username || req.body.username;
|
||||
continueLogin(req, res, next);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUsernameByEmail(req.body.username, next);
|
||||
},
|
||||
function (username, next) {
|
||||
req.body.username = username || req.body.username;
|
||||
continueLogin(req, res, next);
|
||||
},
|
||||
], next);
|
||||
} else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) {
|
||||
continueLogin(req, res, next);
|
||||
} else {
|
||||
@@ -375,7 +365,7 @@ authenticationController.localLogin = function (req, username, password, next) {
|
||||
}
|
||||
|
||||
if (result.banned) {
|
||||
return banUser(uid, next);
|
||||
return getBanInfo(uid, next);
|
||||
}
|
||||
|
||||
user.auth.logAttempt(uid, req.ip, next);
|
||||
@@ -394,48 +384,57 @@ authenticationController.localLogin = function (req, username, password, next) {
|
||||
};
|
||||
|
||||
authenticationController.logout = function (req, res, next) {
|
||||
if (req.user && parseInt(req.user.uid, 10) > 0 && req.sessionID) {
|
||||
var uid = parseInt(req.user.uid, 10);
|
||||
var sessionID = req.sessionID;
|
||||
if (!req.uid || !req.sessionID) {
|
||||
return res.status(200).send('not-logged-in');
|
||||
}
|
||||
|
||||
user.auth.revokeSession(sessionID, uid, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.auth.revokeSession(req.sessionID, req.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
req.logout();
|
||||
req.session.destroy();
|
||||
|
||||
user.setUserField(uid, 'lastonline', Date.now() - 300000);
|
||||
|
||||
plugins.fireHook('static:user.loggedOut', { req: req, res: res, uid: uid }, function () {
|
||||
res.status(200).send('');
|
||||
|
||||
// Force session check for all connected socket.io clients with the same session id
|
||||
sockets.in('sess_' + sessionID).emit('checkSession', 0);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.status(200).send('');
|
||||
}
|
||||
user.setUserField(req.uid, 'lastonline', Date.now() - 300000, next);
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('static:user.loggedOut', { req: req, res: res, uid: req.uid }, next);
|
||||
},
|
||||
function () {
|
||||
// Force session check for all connected socket.io clients with the same session id
|
||||
sockets.in('sess_' + req.sessionID).emit('checkSession', 0);
|
||||
res.status(200).send('');
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
function banUser(uid, next) {
|
||||
user.getLatestBanInfo(uid, function (err, banInfo) {
|
||||
if (err) {
|
||||
if (err.message === 'no-ban-info') {
|
||||
return next(new Error('[[error:user-banned]]'));
|
||||
function getBanInfo(uid, callback) {
|
||||
var banInfo;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getLatestBanInfo(uid, next);
|
||||
},
|
||||
function (_banInfo, next) {
|
||||
banInfo = _banInfo;
|
||||
if (banInfo.reason) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!banInfo.reason) {
|
||||
translator.translate('[[user:info.banned-no-reason]]', function (translated) {
|
||||
banInfo.reason = translated;
|
||||
next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]'));
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
},
|
||||
function (next) {
|
||||
next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]'));
|
||||
},
|
||||
], function (err) {
|
||||
if (err) {
|
||||
if (err.message === 'no-ban-info') {
|
||||
err.message = '[[error:user-banned]]';
|
||||
}
|
||||
}
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
var async = require('async');
|
||||
var request = require('request');
|
||||
var winston = require('winston');
|
||||
|
||||
var db = require('../database');
|
||||
var meta = require('../meta');
|
||||
@@ -138,6 +139,19 @@ module.exports = function (User) {
|
||||
});
|
||||
}
|
||||
|
||||
User.shouldQueueUser = function (ip, callback) {
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
if (registrationType === 'normal' || registrationType === 'invite-only' || registrationType === 'admin-invite-only') {
|
||||
setImmediate(callback, null, false);
|
||||
} else if (registrationType === 'admin-approval') {
|
||||
setImmediate(callback, null, true);
|
||||
} else if (registrationType === 'admin-approval-ip') {
|
||||
db.sortedSetCard('ip:' + ip + ':uid', function (err, count) {
|
||||
callback(err, !!count);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
User.getRegistrationQueue = function (start, stop, callback) {
|
||||
var data;
|
||||
async.waterfall([
|
||||
@@ -152,58 +166,22 @@ module.exports = function (User) {
|
||||
db.getObjects(keys, next);
|
||||
},
|
||||
function (users, next) {
|
||||
users = users.map(function (user, index) {
|
||||
if (user) {
|
||||
user.timestampISO = utils.toISOString(data[index].score);
|
||||
delete user.hashedPassword;
|
||||
}
|
||||
|
||||
users = users.filter(Boolean).map(function (user, index) {
|
||||
user.timestampISO = utils.toISOString(data[index].score);
|
||||
delete user.hashedPassword;
|
||||
return user;
|
||||
}).filter(Boolean);
|
||||
});
|
||||
|
||||
async.map(users, function (user, next) {
|
||||
if (!user) {
|
||||
return next(null, user);
|
||||
}
|
||||
|
||||
// temporary: see http://www.stopforumspam.com/forum/viewtopic.php?id=6392
|
||||
user.ip = user.ip.replace('::ffff:', '');
|
||||
|
||||
async.parallel([
|
||||
function (next) {
|
||||
User.getUidsFromSet('ip:' + user.ip + ':uid', 0, -1, function (err, uids) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
User.getUsersFields(uids, ['uid', 'username', 'picture'], function (err, ipMatch) {
|
||||
user.ipMatch = ipMatch;
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
getIPMatchedUsers(user.ip, next);
|
||||
},
|
||||
function (next) {
|
||||
request({
|
||||
method: 'get',
|
||||
url: 'http://api.stopforumspam.org/api' +
|
||||
'?ip=' + encodeURIComponent(user.ip) +
|
||||
'&email=' + encodeURIComponent(user.email) +
|
||||
'&username=' + encodeURIComponent(user.username) +
|
||||
'&f=json',
|
||||
json: true,
|
||||
}, function (err, response, body) {
|
||||
if (err) {
|
||||
return next();
|
||||
}
|
||||
if (response.statusCode === 200 && body) {
|
||||
user.spamData = body;
|
||||
user.usernameSpam = body.username ? (body.username.frequency > 0 || body.username.appears > 0) : true;
|
||||
user.emailSpam = body.email ? (body.email.frequency > 0 || body.email.appears > 0) : true;
|
||||
user.ipSpam = body.ip ? (body.ip.frequency > 0 || body.ip.appears > 0) : true;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
getSpamData(user, next);
|
||||
},
|
||||
], function (err) {
|
||||
next(err, user);
|
||||
@@ -218,4 +196,45 @@ module.exports = function (User) {
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
function getIPMatchedUsers(ip, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
User.getUidsFromSet('ip:' + ip + ':uid', 0, -1, next);
|
||||
},
|
||||
function (uids, next) {
|
||||
User.getUsersFields(uids, ['uid', 'username', 'picture'], next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function getSpamData(user, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
request({
|
||||
method: 'get',
|
||||
url: 'http://api.stopforumspam.org/api' +
|
||||
'?ip=' + encodeURIComponent(user.ip) +
|
||||
'&email=' + encodeURIComponent(user.email) +
|
||||
'&username=' + encodeURIComponent(user.username) +
|
||||
'&f=json',
|
||||
json: true,
|
||||
}, next);
|
||||
},
|
||||
function (response, body, next) {
|
||||
if (response.statusCode === 200 && body) {
|
||||
user.spamData = body;
|
||||
user.usernameSpam = body.username ? (body.username.frequency > 0 || body.username.appears > 0) : true;
|
||||
user.emailSpam = body.email ? (body.email.frequency > 0 || body.email.appears > 0) : true;
|
||||
user.ipSpam = body.ip ? (body.ip.frequency > 0 || body.ip.appears > 0) : true;
|
||||
}
|
||||
next();
|
||||
},
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -118,22 +118,28 @@ module.exports = function (User) {
|
||||
User.auth.revokeSession = function (sessionId, uid, callback) {
|
||||
winston.verbose('[user.auth] Revoking session ' + sessionId + ' for user ' + uid);
|
||||
|
||||
db.sessionStore.get(sessionId, function (err, sessionObj) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.parallel([
|
||||
function (next) {
|
||||
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid) {
|
||||
db.deleteObjectField('uid:' + uid + ':sessionUUID:sessionId', sessionObj.meta.uuid, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
async.apply(db.sortedSetRemove, 'uid:' + uid + ':sessions', sessionId),
|
||||
async.apply(db.sessionStore.destroy.bind(db.sessionStore), sessionId),
|
||||
], callback);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sessionStore.get(sessionId, function (err, sessionObj) {
|
||||
next(err, sessionObj || null);
|
||||
});
|
||||
},
|
||||
function (sessionObj, next) {
|
||||
async.parallel([
|
||||
function (next) {
|
||||
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid) {
|
||||
db.deleteObjectField('uid:' + uid + ':sessionUUID:sessionId', sessionObj.meta.uuid, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
async.apply(db.sortedSetRemove, 'uid:' + uid + ':sessions', sessionId),
|
||||
async.apply(db.sessionStore.destroy.bind(db.sessionStore), sessionId),
|
||||
], function (err) {
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.auth.revokeAllSessions = function (uid, callback) {
|
||||
|
||||
@@ -88,6 +88,7 @@ describe('authentication', function () {
|
||||
email: 'admin@nodebb.org',
|
||||
username: 'admin',
|
||||
password: 'adminpwd',
|
||||
userLang: 'it',
|
||||
},
|
||||
json: true,
|
||||
jar: jar,
|
||||
@@ -107,7 +108,11 @@ describe('authentication', function () {
|
||||
assert(body);
|
||||
assert.equal(body.username, 'admin');
|
||||
assert.equal(body.email, 'admin@nodebb.org');
|
||||
done();
|
||||
user.getSettings(body.uid, function (err, settings) {
|
||||
assert.ifError(err);
|
||||
assert.equal(settings.userLang, 'it');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -298,6 +303,92 @@ describe('authentication', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue user if ip is used before', function (done) {
|
||||
meta.config.registrationType = 'admin-approval-ip';
|
||||
registerUser('another@user.com', 'anotheruser', 'anotherpwd', function (err, response, body) {
|
||||
meta.config.registrationType = 'normal';
|
||||
assert.ifError(err);
|
||||
assert.equal(response.statusCode, 200);
|
||||
assert.equal(body.message, '[[register:registration-added-to-queue]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should be able to login with email', function (done) {
|
||||
user.create({ username: 'ginger', password: '123456', email: 'ginger@nodebb.org' }, function (err, uid) {
|
||||
assert.ifError(err);
|
||||
loginUser('ginger@nodebb.org', '123456', function (err, response, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(response.statusCode, 200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to login if login type is username and an email is sent', function (done) {
|
||||
meta.config.allowLoginWith = 'username';
|
||||
loginUser('ginger@nodebb.org', '123456', function (err, response, body) {
|
||||
meta.config.allowLoginWith = 'username-email';
|
||||
assert.ifError(err);
|
||||
assert.equal(response.statusCode, 500);
|
||||
assert.equal(body, '[[error:wrong-login-type-username]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send 200 if not logged in', function (done) {
|
||||
var jar = request.jar();
|
||||
request({
|
||||
url: nconf.get('url') + '/api/config',
|
||||
json: true,
|
||||
jar: jar,
|
||||
}, function (err, response, body) {
|
||||
assert.ifError(err);
|
||||
|
||||
request.post(nconf.get('url') + '/logout', {
|
||||
form: {},
|
||||
json: true,
|
||||
jar: jar,
|
||||
headers: {
|
||||
'x-csrf-token': body.csrf_token,
|
||||
},
|
||||
}, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.equal(body, 'not-logged-in');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should prevent banned user from logging in', function (done) {
|
||||
user.create({ username: 'banme', password: '123456', email: 'ban@me.com' }, function (err, uid) {
|
||||
assert.ifError(err);
|
||||
user.ban(uid, 0, 'spammer', function (err) {
|
||||
assert.ifError(err);
|
||||
loginUser('banme', '123456', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.equal(body, '[[error:user-banned-reason, spammer]]');
|
||||
user.unban(uid, function (err) {
|
||||
assert.ifError(err);
|
||||
var expiry = Date.now() + 10000;
|
||||
user.ban(uid, expiry, '', function (err) {
|
||||
assert.ifError(err);
|
||||
loginUser('banme', '123456', function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.equal(body, '[[error:user-banned-reason-until, ' + (new Date(parseInt(expiry, 10)).toString()) + ', No reason given.]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
db.emptydb(done);
|
||||
});
|
||||
|
||||
@@ -1384,7 +1384,7 @@ describe('User', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should send digetst', function (done) {
|
||||
it('should send digest', function (done) {
|
||||
db.sortedSetAdd('digest:day:uids', [Date.now(), Date.now()], [1, 2], function (err) {
|
||||
assert.ifError(err);
|
||||
User.digest.execute('day', function (err) {
|
||||
|
||||
Reference in New Issue
Block a user