v1.18.x
Julian Lam 10 years ago
parent 43502e041a
commit 56fc958939

@ -12,5 +12,6 @@
"password_reset_sent": "Password Reset Sent",
"invalid_email": "Invalid Email / Email does not exist!",
"password_too_short": "The password entered is too short, please pick a different password.",
"passwords_do_not_match": "The two passwords you've entered do not match."
"passwords_do_not_match": "The two passwords you've entered do not match.",
"password_expired": "Your password has expired, please choose a new password"
}

@ -11,7 +11,7 @@ define('forum/reset', function() {
$('#reset').on('click', function() {
if (inputEl.val() && inputEl.val().indexOf('@') !== -1) {
socket.emit('user.reset.send', inputEl.val(), function(err, data) {
socket.emit('user.reset.send', inputEl.val(), function(err) {
if(err) {
return app.alertError(err.message);
}

@ -57,9 +57,12 @@ Controllers.reset = function(req, res, next) {
}
res.render('reset_code', {
valid: valid,
displayExpiryNotice: req.session.passwordExpired,
code: req.params.code ? req.params.code : null,
breadcrumbs: helpers.buildBreadcrumbs([{text: '[[reset_password:reset_password]]', url: '/reset'}, {text: '[[reset_password:update_password]]'}])
});
delete req.session.passwordExpired;
});
} else {
res.render('reset', {

@ -87,7 +87,7 @@
}
var userslug = utils.slugify(username);
var uid;
var uid, userData = {};
async.waterfall([
function(next) {
@ -101,9 +101,12 @@
user.auth.logAttempt(uid, req.ip, next);
},
function(next) {
db.getObjectFields('user:' + uid, ['password', 'banned'], next);
db.getObjectFields('user:' + uid, ['password', 'banned', 'passwordExpiry'], next);
},
function(userData, next) {
function(_userData, next) {
userData = _userData;
userData.uid = uid;
if (!userData || !userData.password) {
return next(new Error('[[error:invalid-user-data]]'));
}
@ -117,7 +120,7 @@
return next(new Error('[[error:invalid-password]]'));
}
user.auth.clearLoginAttempts(uid);
next(null, {uid: uid}, '[[success:authentication-successful]]');
next(null, userData, '[[success:authentication-successful]]');
}
], next);
};
@ -165,6 +168,8 @@
Auth.continueLogin = function(req, res, next) {
passport.authenticate('local', function(err, userData, info) {
var passwordExpiry = userData.passwordExpiry !== undefined ? parseInt(userData.passwordExpiry, 10) : null;
if (err) {
return res.status(403).send(err.message);
}
@ -187,28 +192,35 @@
req.session.cookie.expires = false;
}
req.login({
uid: userData.uid
}, function(err) {
if (err) {
return res.status(403).send(err.message);
}
if (userData.uid) {
user.logIP(userData.uid, req.ip);
plugins.fireHook('action:user.loggedIn', userData.uid);
}
if (passwordExpiry && passwordExpiry < Date.now()) {
winston.verbose('[auth] Triggering password reset for uid ' + userData.uid + ' due to password policy');
req.session.passwordExpired = true;
user.reset.generate(userData.uid, function(err, code) {
res.status(200).send(nconf.get('relative_path') + '/reset/' + code);
});
} else {
req.login({
uid: userData.uid
}, function(err) {
if (err) {
return res.status(403).send(err.message);
}
if (userData.uid) {
user.logIP(userData.uid, req.ip);
if (!req.session.returnTo) {
res.status(200).send(nconf.get('relative_path') + '/');
} else {
plugins.fireHook('action:user.loggedIn', userData.uid);
}
var next = req.session.returnTo;
delete req.session.returnTo;
if (!req.session.returnTo) {
res.status(200).send(nconf.get('relative_path') + '/');
} else {
var next = req.session.returnTo;
delete req.session.returnTo;
res.status(200).send(next);
}
});
res.status(200).send(next);
}
});
}
})(req, res, next);
};

@ -126,12 +126,16 @@ module.exports = function(User) {
if (!data.password) {
return next();
}
User.hashPassword(data.password, function(err, hash) {
if (err) {
return next(err);
}
User.setUserField(userData.uid, 'password', hash, next);
async.parallel([
async.apply(User.setUserField, userData.uid, 'password', hash),
async.apply(User.reset.updateExpiry, userData.uid)
], next);
});
}
], function(err) {

@ -257,7 +257,10 @@ module.exports = function(User) {
return callback(err);
}
User.setUserField(data.uid, 'password', hash, callback);
async.parallel([
async.apply(User.setUserField, data.uid, 'password', hash),
async.apply(User.reset.updateExpiry, data.uid)
], callback);
});
}

@ -32,8 +32,17 @@ var async = require('async'),
], callback);
};
UserReset.generate = function(uid, callback) {
var code = utils.generateUUID();
async.parallel([
async.apply(db.setObjectField, 'reset:uid', code, uid),
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code)
], function(err) {
callback(err, code);
});
};
UserReset.send = function(email, callback) {
var reset_code = utils.generateUUID();
var uid;
async.waterfall([
function(next) {
@ -45,18 +54,15 @@ var async = require('async'),
}
uid = _uid;
async.parallel([
async.apply(db.setObjectField, 'reset:uid', reset_code, uid),
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), reset_code)
], next);
UserReset.generate(uid, next);
},
function(results, next) {
function(code, next) {
translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function(subject) {
next(null, subject);
next(null, subject, code);
});
},
function(subject, next) {
var reset_link = nconf.get('url') + '/reset/' + reset_code;
function(subject, code, next) {
var reset_link = nconf.get('url') + '/reset/' + code;
emailer.send('reset', uid, {
site_title: (meta.config.title || 'NodeBB'),
reset_link: reset_link,
@ -64,9 +70,6 @@ var async = require('async'),
template: 'reset',
uid: uid
}, next);
},
function(next) {
next(null, reset_code);
}
], callback);
};
@ -96,12 +99,22 @@ var async = require('async'),
async.apply(user.setUserField, uid, 'password', hash),
async.apply(db.deleteObjectField, 'reset:uid', code),
async.apply(db.sortedSetRemove, 'reset:issueDate', code),
async.apply(user.reset.updateExpiry, uid),
async.apply(user.auth.resetLockout, uid)
], next);
}
], callback);
};
UserReset.updateExpiry = function(uid, callback) {
var oneDay = 1000*60*60*24,
expireDays = parseInt(meta.config.passwordExpiryDays || 0, 10),
expiry = Date.now() + (oneDay * expireDays);
callback = callback || function() {};
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
};
UserReset.clean = function(callback) {
async.waterfall([
async.apply(db.getSortedSetRangeByScore, 'reset:issueDate', 0, -1, 0, Date.now() - twoHours),

@ -102,6 +102,10 @@
<label>Days to remember user login sessions</label>
<input type="text" class="form-control" data-field="loginDays" placeholder="14" />
</div>
<div class="form-group">
<label>Force password reset after a set number of days</label>
<input type="text" class="form-control" data-field="passwordExpiryDays" placeholder="0" />
</div>
</form>
</div>
</div>

Loading…
Cancel
Save