WIP on OTP 2FA, stats, etc.

This commit is contained in:
Bryan Ashby 2019-05-07 21:36:33 -06:00
parent e960e2800d
commit e5398db07b
No known key found for this signature in database
GPG Key ID: B49EB437951D2542
2 changed files with 59 additions and 45 deletions

View File

@ -51,4 +51,5 @@ exports.ErrorReasons = {
Inactive : 'INACTIVE', Inactive : 'INACTIVE',
Locked : 'LOCKED', Locked : 'LOCKED',
NotAllowed : 'NOTALLOWED', NotAllowed : 'NOTALLOWED',
Invalid2FA : 'INVALID2FA',
}; };

View File

@ -21,7 +21,9 @@ const User = require('./user.js');
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
exports.userLogin = userLogin; exports.userLogin = userLogin;
exports.recordLogin = recordLogin;
exports.transformLoginError = transformLoginError;
function userLogin(client, username, password, options, cb) { function userLogin(client, username, password, options, cb) {
if(!cb && _.isFunction(options)) { if(!cb && _.isFunction(options)) {
@ -50,20 +52,13 @@ function userLogin(client, username, password, options, cb) {
client.user.authenticateFactor1(authInfo, err => { client.user.authenticateFactor1(authInfo, err => {
if(err) { if(err) {
client.user.sessionFailedLoginAttempts = _.get(client.user, 'sessionFailedLoginAttempts', 0) + 1; return cb(transformLoginError(err, client, username));
const disconnect = config.users.failedLogin.disconnect;
if(disconnect > 0 && client.user.sessionFailedLoginAttempts >= disconnect) {
err = Errors.BadLogin('To many failed login attempts', ErrorReasons.TooMany);
}
client.log.info( { username, ip : client.remoteAddress, reason : err.message }, 'Failed login attempt');
return cb(err);
} }
const user = client.user; const user = client.user;
// Good login; reset any failed attempts // Good login; reset any failed attempts
delete user.sessionFailedLoginAttempts; delete client.sessionFailedLoginAttempts;
// //
// Ensure this user is not already logged in. // Ensure this user is not already logged in.
@ -104,41 +99,59 @@ function userLogin(client, username, password, options, cb) {
Events.emit(Events.getSystemEvents().UserLogin, { user } ); Events.emit(Events.getSystemEvents().UserLogin, { user } );
async.parallel( setClientTheme(client, user.properties[UserProps.ThemeId]);
[ if(user.authenticated) {
function setTheme(callback) { return recordLogin(client, cb);
setClientTheme(client, user.properties[UserProps.ThemeId]); }
return callback(null);
},
function updateSystemLoginCount(callback) {
StatLog.incrementNonPersistentSystemStat(SysProps.LoginsToday, 1);
return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback);
},
function recordLastLogin(callback) {
return StatLog.setUserStat(user, UserProps.LastLoginTs, StatLog.now, callback);
},
function updateUserLoginCount(callback) {
return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback);
},
function recordLoginHistory(callback) {
const loginHistoryMax = Config().statLog.systemEvents.loginHistoryMax;
const historyItem = JSON.stringify({
userId : user.userId,
sessionId : user.sessionId,
});
return StatLog.appendSystemLogEntry( // recordLogin() must happen after 2FA!
SystemLogKeys.UserLoginHistory, return cb(null);
historyItem,
loginHistoryMax,
StatLog.KeepType.Max,
callback
);
}
],
err => {
return cb(err);
}
);
}); });
} }
function recordLogin(client, cb) {
const user = client.user;
async.parallel(
[
(callback) => {
StatLog.incrementNonPersistentSystemStat(SysProps.LoginsToday, 1);
return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback);
},
(callback) => {
return StatLog.setUserStat(user, UserProps.LastLoginTs, StatLog.now, callback);
},
(callback) => {
return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback);
},
(callback) => {
const loginHistoryMax = Config().statLog.systemEvents.loginHistoryMax;
const historyItem = JSON.stringify({
userId : user.userId,
sessionId : user.sessionId,
});
return StatLog.appendSystemLogEntry(
SystemLogKeys.UserLoginHistory,
historyItem,
loginHistoryMax,
StatLog.KeepType.Max,
callback
);
}
],
err => {
return cb(err);
}
);
}
function transformLoginError(err, client, username) {
client.sessionFailedLoginAttempts = _.get(client, 'sessionFailedLoginAttempts', 0) + 1;
const disconnect = Config().users.failedLogin.disconnect;
if(disconnect > 0 && client.sessionFailedLoginAttempts >= disconnect) {
err = Errors.BadLogin('To many failed login attempts', ErrorReasons.TooMany);
}
client.log.info( { username, ip : client.remoteAddress, reason : err.message }, 'Failed login attempt');
return err;
}