You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/user/auth.js

181 lines
5.0 KiB
JavaScript

11 years ago
'use strict';
9 years ago
var async = require('async');
var winston = require('winston');
var db = require('../database');
var meta = require('../meta');
var events = require('../events');
8 years ago
var batch = require('../batch');
11 years ago
module.exports = function (User) {
11 years ago
User.auth = {};
User.auth.logAttempt = function (uid, ip, callback) {
if (!parseInt(uid, 10)) {
return setImmediate(callback);
}
8 years ago
async.waterfall([
function (next) {
db.exists('lockout:' + uid, next);
},
function (exists, next) {
if (exists) {
return callback(new Error('[[error:account-locked]]'));
11 years ago
}
8 years ago
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);
11 years ago
}
8 years ago
// 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,
8 years ago
});
next(new Error('[[error:account-locked]]'));
},
8 years ago
], callback);
11 years ago
};
User.auth.clearLoginAttempts = function (uid) {
11 years ago
db.delete('loginAttempts:' + uid);
};
11 years ago
User.auth.resetLockout = function (uid, callback) {
11 years ago
async.parallel([
async.apply(db.delete, 'loginAttempts:' + uid),
async.apply(db.delete, 'lockout:' + uid),
11 years ago
], callback);
};
User.auth.getSessions = function (uid, curSessionId, callback) {
var _sids;
// curSessionId is optional
if (arguments.length === 2 && typeof curSessionId === 'function') {
callback = curSessionId;
curSessionId = undefined;
}
async.waterfall([
async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':sessions', 0, 19),
9 years ago
function (sids, next) {
_sids = sids;
async.map(sids, db.sessionStore.get.bind(db.sessionStore), next);
},
9 years ago
function (sessions, next) {
sessions.forEach(function (sessionObj, idx) {
9 years ago
if (sessionObj && sessionObj.meta) {
sessionObj.meta.current = curSessionId === _sids[idx];
}
});
// Revoke any sessions that have expired, return filtered list
var expiredSids = [];
var expired;
sessions = sessions.filter(function (sessionObj, idx) {
9 years ago
expired = !sessionObj || !sessionObj.hasOwnProperty('passport') ||
!sessionObj.passport.hasOwnProperty('user') ||
parseInt(sessionObj.passport.user, 10) !== parseInt(uid, 10);
if (expired) {
expiredSids.push(_sids[idx]);
}
return !expired;
9 years ago
});
async.each(expiredSids, function (sid, next) {
User.auth.revokeSession(sid, uid, next);
}, function (err) {
next(err, sessions);
});
},
9 years ago
], function (err, sessions) {
callback(err, sessions ? sessions.map(function (sessObj) {
sessObj.meta.datetimeISO = new Date(sessObj.meta.datetime).toISOString();
return sessObj.meta;
}) : undefined);
});
};
User.auth.addSession = function (uid, sessionId, callback) {
callback = callback || function () {};
db.sortedSetAdd('uid:' + uid + ':sessions', Date.now(), sessionId, callback);
};
User.auth.revokeSession = function (sessionId, uid, callback) {
winston.verbose('[user.auth] Revoking session ' + sessionId + ' for user ' + uid);
db.sessionStore.get(sessionId, function (err, sessionObj) {
9 years ago
if (err) {
return callback(err);
}
async.parallel([
9 years ago
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);
});
};
User.auth.revokeAllSessions = function (uid, callback) {
async.waterfall([
async.apply(db.getSortedSetRange, 'uid:' + uid + ':sessions', 0, -1),
function (sids, next) {
async.each(sids, function (sid, next) {
User.auth.revokeSession(sid, uid, next);
}, next);
},
], callback);
};
8 years ago
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);
},
8 years ago
], next);
},
8 years ago
], next);
}, { batch: 1000 }, callback);
8 years ago
};
8 years ago
};