mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-03 04:25:55 +01:00
more auth tests
This commit is contained in:
@@ -52,19 +52,7 @@ authenticationController.register = function (req, res) {
|
|||||||
user.isPasswordValid(userData.password, next);
|
user.isPasswordValid(userData.password, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
if (registrationType === 'normal' || registrationType === 'invite-only' || registrationType === 'admin-invite-only') {
|
user.shouldQueueUser(req.ip, next);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
function (queue, next) {
|
function (queue, next) {
|
||||||
res.locals.processLogin = true; // set it to false in plugin if you wish to just register only
|
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';
|
var loginWith = meta.config.allowLoginWith || 'username-email';
|
||||||
|
|
||||||
if (req.body.username && utils.isEmailValid(req.body.username) && loginWith.indexOf('email') !== -1) {
|
if (req.body.username && utils.isEmailValid(req.body.username) && loginWith.indexOf('email') !== -1) {
|
||||||
user.getUsernameByEmail(req.body.username, function (err, username) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return next(err);
|
user.getUsernameByEmail(req.body.username, next);
|
||||||
}
|
},
|
||||||
req.body.username = username || req.body.username;
|
function (username, next) {
|
||||||
continueLogin(req, res, next);
|
req.body.username = username || req.body.username;
|
||||||
});
|
continueLogin(req, res, next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
} else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) {
|
} else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) {
|
||||||
continueLogin(req, res, next);
|
continueLogin(req, res, next);
|
||||||
} else {
|
} else {
|
||||||
@@ -375,7 +365,7 @@ authenticationController.localLogin = function (req, username, password, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result.banned) {
|
if (result.banned) {
|
||||||
return banUser(uid, next);
|
return getBanInfo(uid, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.auth.logAttempt(uid, req.ip, 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) {
|
authenticationController.logout = function (req, res, next) {
|
||||||
if (req.user && parseInt(req.user.uid, 10) > 0 && req.sessionID) {
|
if (!req.uid || !req.sessionID) {
|
||||||
var uid = parseInt(req.user.uid, 10);
|
return res.status(200).send('not-logged-in');
|
||||||
var sessionID = req.sessionID;
|
}
|
||||||
|
|
||||||
user.auth.revokeSession(sessionID, uid, function (err) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return next(err);
|
user.auth.revokeSession(req.sessionID, req.uid, next);
|
||||||
}
|
},
|
||||||
|
function (next) {
|
||||||
req.logout();
|
req.logout();
|
||||||
req.session.destroy();
|
req.session.destroy();
|
||||||
|
|
||||||
user.setUserField(uid, 'lastonline', Date.now() - 300000);
|
user.setUserField(req.uid, 'lastonline', Date.now() - 300000, next);
|
||||||
|
},
|
||||||
plugins.fireHook('static:user.loggedOut', { req: req, res: res, uid: uid }, function () {
|
function (next) {
|
||||||
res.status(200).send('');
|
plugins.fireHook('static:user.loggedOut', { req: req, res: res, uid: req.uid }, next);
|
||||||
|
},
|
||||||
// Force session check for all connected socket.io clients with the same session id
|
function () {
|
||||||
sockets.in('sess_' + sessionID).emit('checkSession', 0);
|
// 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('');
|
||||||
} else {
|
},
|
||||||
res.status(200).send('');
|
], next);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function banUser(uid, next) {
|
function getBanInfo(uid, callback) {
|
||||||
user.getLatestBanInfo(uid, function (err, banInfo) {
|
var banInfo;
|
||||||
if (err) {
|
async.waterfall([
|
||||||
if (err.message === 'no-ban-info') {
|
function (next) {
|
||||||
return next(new Error('[[error:user-banned]]'));
|
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) {
|
translator.translate('[[user:info.banned-no-reason]]', function (translated) {
|
||||||
banInfo.reason = 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 + ']]'));
|
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 async = require('async');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
|
var winston = require('winston');
|
||||||
|
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
var meta = require('../meta');
|
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) {
|
User.getRegistrationQueue = function (start, stop, callback) {
|
||||||
var data;
|
var data;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
@@ -152,58 +166,22 @@ module.exports = function (User) {
|
|||||||
db.getObjects(keys, next);
|
db.getObjects(keys, next);
|
||||||
},
|
},
|
||||||
function (users, next) {
|
function (users, next) {
|
||||||
users = users.map(function (user, index) {
|
users = users.filter(Boolean).map(function (user, index) {
|
||||||
if (user) {
|
user.timestampISO = utils.toISOString(data[index].score);
|
||||||
user.timestampISO = utils.toISOString(data[index].score);
|
delete user.hashedPassword;
|
||||||
delete user.hashedPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}).filter(Boolean);
|
});
|
||||||
|
|
||||||
async.map(users, function (user, next) {
|
async.map(users, function (user, next) {
|
||||||
if (!user) {
|
|
||||||
return next(null, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// temporary: see http://www.stopforumspam.com/forum/viewtopic.php?id=6392
|
// temporary: see http://www.stopforumspam.com/forum/viewtopic.php?id=6392
|
||||||
user.ip = user.ip.replace('::ffff:', '');
|
user.ip = user.ip.replace('::ffff:', '');
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function (next) {
|
function (next) {
|
||||||
User.getUidsFromSet('ip:' + user.ip + ':uid', 0, -1, function (err, uids) {
|
getIPMatchedUsers(user.ip, next);
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
User.getUsersFields(uids, ['uid', 'username', 'picture'], function (err, ipMatch) {
|
|
||||||
user.ipMatch = ipMatch;
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
request({
|
getSpamData(user, next);
|
||||||
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();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
], function (err) {
|
], function (err) {
|
||||||
next(err, user);
|
next(err, user);
|
||||||
@@ -218,4 +196,45 @@ module.exports = function (User) {
|
|||||||
},
|
},
|
||||||
], callback);
|
], 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) {
|
User.auth.revokeSession = function (sessionId, uid, callback) {
|
||||||
winston.verbose('[user.auth] Revoking session ' + sessionId + ' for user ' + uid);
|
winston.verbose('[user.auth] Revoking session ' + sessionId + ' for user ' + uid);
|
||||||
|
|
||||||
db.sessionStore.get(sessionId, function (err, sessionObj) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.sessionStore.get(sessionId, function (err, sessionObj) {
|
||||||
}
|
next(err, sessionObj || null);
|
||||||
async.parallel([
|
});
|
||||||
function (next) {
|
},
|
||||||
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid) {
|
function (sessionObj, next) {
|
||||||
db.deleteObjectField('uid:' + uid + ':sessionUUID:sessionId', sessionObj.meta.uuid, next);
|
async.parallel([
|
||||||
} else {
|
function (next) {
|
||||||
next();
|
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid) {
|
||||||
}
|
db.deleteObjectField('uid:' + uid + ':sessionUUID:sessionId', sessionObj.meta.uuid, next);
|
||||||
},
|
} else {
|
||||||
async.apply(db.sortedSetRemove, 'uid:' + uid + ':sessions', sessionId),
|
next();
|
||||||
async.apply(db.sessionStore.destroy.bind(db.sessionStore), sessionId),
|
}
|
||||||
], callback);
|
},
|
||||||
});
|
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) {
|
User.auth.revokeAllSessions = function (uid, callback) {
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ describe('authentication', function () {
|
|||||||
email: 'admin@nodebb.org',
|
email: 'admin@nodebb.org',
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: 'adminpwd',
|
password: 'adminpwd',
|
||||||
|
userLang: 'it',
|
||||||
},
|
},
|
||||||
json: true,
|
json: true,
|
||||||
jar: jar,
|
jar: jar,
|
||||||
@@ -107,7 +108,11 @@ describe('authentication', function () {
|
|||||||
assert(body);
|
assert(body);
|
||||||
assert.equal(body.username, 'admin');
|
assert.equal(body.username, 'admin');
|
||||||
assert.equal(body.email, 'admin@nodebb.org');
|
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) {
|
after(function (done) {
|
||||||
db.emptydb(done);
|
db.emptydb(done);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@ describe('User', function () {
|
|||||||
done();
|
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) {
|
db.sortedSetAdd('digest:day:uids', [Date.now(), Date.now()], [1, 2], function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
User.digest.execute('day', function (err) {
|
User.digest.execute('day', function (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user