mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 22:11:09 +03:00
Adds login limiter
Closes #499 * On wrong passwords, statuses: `active` -> `warn-1` -> `warn-2` -> `warn-3` -> `locked` * On login check, if user's status is `locked`, login automatically fails and user is encouraged to reset password. Does not even bother to check for passwords. * login attempts tell user how many attempts she has remaining in notification box * successful login will reset status to `active` * resetting password with forgotten password emailed token resets status to `active` * complete with a test suite
This commit is contained in:
parent
e4a5356a69
commit
c515e20ea3
@ -155,17 +155,53 @@ User = ghostBookshelf.Model.extend({
|
||||
|
||||
},
|
||||
|
||||
setWarning: function (user) {
|
||||
var status = user.get('status'),
|
||||
regexp = /warn-(\d+)/i,
|
||||
level;
|
||||
|
||||
if (status === 'active') {
|
||||
user.set('status', 'warn-1');
|
||||
level = 1;
|
||||
} else {
|
||||
level = parseInt(status.match(regexp)[1], 10) + 1;
|
||||
if (level > 3) {
|
||||
user.set('status', 'locked');
|
||||
} else {
|
||||
user.set('status', 'warn-' + level);
|
||||
}
|
||||
}
|
||||
return when(user.save()).then(function () {
|
||||
return 5 - level;
|
||||
});
|
||||
},
|
||||
|
||||
// Finds the user by email, and checks the password
|
||||
check: function (_userdata) {
|
||||
var self = this,
|
||||
s;
|
||||
return this.forge({
|
||||
email: _userdata.email.toLocaleLowerCase()
|
||||
}).fetch({require: true}).then(function (user) {
|
||||
if (user.get('status') !== 'locked') {
|
||||
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
|
||||
if (!matched) {
|
||||
return when.reject(new Error('Your password is incorrect'));
|
||||
return when(self.setWarning(user)).then(function (remaining) {
|
||||
s = (remaining > 1) ? 's' : '';
|
||||
return when.reject(new Error('Your password is incorrect.<br>' +
|
||||
remaining + ' attempt' + s + ' remaining!'));
|
||||
});
|
||||
}
|
||||
|
||||
return when(user.set('status', 'active').save()).then(function (user) {
|
||||
return user;
|
||||
});
|
||||
}, errors.logAndThrowError);
|
||||
}
|
||||
return when.reject(new Error('Your account is locked due to too many ' +
|
||||
'login attempts. Please reset your password to log in again by clicking ' +
|
||||
'the "Forgotten password?" link!'));
|
||||
|
||||
}, function (error) {
|
||||
/*jslint unparam:true*/
|
||||
return when.reject(new Error('There is no user with that email address.'));
|
||||
@ -285,7 +321,7 @@ User = ghostBookshelf.Model.extend({
|
||||
var foundUser = results[0],
|
||||
passwordHash = results[1];
|
||||
|
||||
foundUser.save({password: passwordHash});
|
||||
foundUser.save({password: passwordHash, status: 'active'});
|
||||
|
||||
return foundUser;
|
||||
});
|
||||
|
@ -74,6 +74,32 @@ CasperTest.begin("Can't spam it", 3, function suite(test) {
|
||||
casper.wait(2000);
|
||||
}, true);
|
||||
|
||||
|
||||
CasperTest.begin("Login limit is in place", 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/signin/", function testTitle() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
});
|
||||
|
||||
casper.waitForOpaque(".login-box",
|
||||
function then() {
|
||||
this.fill("#login", falseUser, true);
|
||||
},
|
||||
function onTimeout() {
|
||||
test.fail('Sign in form didn\'t fade in.');
|
||||
});
|
||||
|
||||
casper.wait(2100, function doneWait() {
|
||||
this.fill("#login", falseUser, true);
|
||||
});
|
||||
|
||||
casper.waitForText('remaining', function onSuccess() {
|
||||
test.assert(true, 'The login limit is in place.');
|
||||
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
|
||||
}, function onTimeout() {
|
||||
test.assert(false, 'We did not trip the login limit.');
|
||||
});
|
||||
}, true);
|
||||
|
||||
CasperTest.begin("Can login to Ghost", 4, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/login/", function testTitle() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
|
Loading…
Reference in New Issue
Block a user