Add bcrypt validation
This commit is contained in:
parent
899ab457e9
commit
7aebf0d132
|
@ -14,7 +14,7 @@ var utils = require('../../lib/utils');
|
|||
var path = require('path');
|
||||
var SALT_WORK_FACTOR = 10;
|
||||
var crypto = require('crypto');
|
||||
|
||||
var MAX_PASSWORD_LENGTH = 72;
|
||||
var bcrypt;
|
||||
try {
|
||||
// Try the native module first
|
||||
|
@ -548,7 +548,6 @@ module.exports = function(User) {
|
|||
cb = cb || utils.createPromiseCallback();
|
||||
var UserModel = this;
|
||||
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
|
||||
|
||||
options = options || {};
|
||||
if (typeof options.email !== 'string') {
|
||||
var err = new Error(g.f('Email is required'));
|
||||
|
@ -558,6 +557,13 @@ module.exports = function(User) {
|
|||
return cb.promise;
|
||||
}
|
||||
|
||||
try {
|
||||
if (options.password) {
|
||||
UserModel.validatePassword(options.password);
|
||||
}
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
UserModel.findOne({ where: { email: options.email }}, function(err, user) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
|
@ -596,14 +602,20 @@ module.exports = function(User) {
|
|||
};
|
||||
|
||||
User.validatePassword = function(plain) {
|
||||
if (typeof plain === 'string' && plain) {
|
||||
var err;
|
||||
if (plain && typeof plain === 'string' && plain.length <= MAX_PASSWORD_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
var err = new Error(g.f('Invalid password: %s', plain));
|
||||
if (plain.length > MAX_PASSWORD_LENGTH) {
|
||||
err = new Error (g.f('Password too long: %s', plain));
|
||||
err.code = 'PASSWORD_TOO_LONG';
|
||||
} else {
|
||||
err = new Error(g.f('Invalid password: %s', plain));
|
||||
err.code = 'INVALID_PASSWORD';
|
||||
}
|
||||
err.statusCode = 422;
|
||||
throw err;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Setup an extended user model.
|
||||
*/
|
||||
|
|
2
index.js
2
index.js
|
@ -4,7 +4,7 @@
|
|||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
var SG = require('strong-globalize');
|
||||
SG.SetRootDir(__dirname, {autonomousMsgLoading: 'all'});
|
||||
SG.SetRootDir(__dirname, { autonomousMsgLoading: 'all' });
|
||||
|
||||
/**
|
||||
* loopback ~ public api
|
||||
|
|
|
@ -363,6 +363,68 @@ describe('User', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Password length validation', function() {
|
||||
var pass72Char = new Array(70).join('a') + '012';
|
||||
var pass73Char = pass72Char + '3';
|
||||
var passTooLong = pass72Char + 'WXYZ1234';
|
||||
|
||||
it('rejects passwords longer than 72 characters', function(done) {
|
||||
try {
|
||||
User.create({ email: 'b@c.com', password: pass73Char }, function(err) {
|
||||
if (err) return done (err);
|
||||
done(new Error('User.create() should have thrown an error.'));
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).to.match(/Password too long/);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a new user with password longer than 72 characters', function(done) {
|
||||
try {
|
||||
var u = new User({ username: 'foo', password: pass73Char });
|
||||
assert(false, 'Error should have been thrown');
|
||||
} catch (e) {
|
||||
expect(e).to.match(/Password too long/);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('accepts passwords that are exactly 72 characters long', function(done) {
|
||||
User.create({ email: 'b@c.com', password: pass72Char }, function(err, user) {
|
||||
if (err) return done(err);
|
||||
User.findById(user.id, function(err, userFound) {
|
||||
if (err) return done (err);
|
||||
assert(userFound);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('allows login with password exactly 72 characters long', function(done) {
|
||||
User.create({ email: 'b@c.com', password: pass72Char }, function(err) {
|
||||
if (err) return done(err);
|
||||
User.login({ email: 'b@c.com', password: pass72Char }, function(err, accessToken) {
|
||||
if (err) return done(err);
|
||||
assertGoodToken(accessToken);
|
||||
assert(accessToken.id);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects password reset when password is more than 72 chars', function(done) {
|
||||
User.create({ email: 'b@c.com', password: pass72Char }, function(err) {
|
||||
if (err) return done (err);
|
||||
User.resetPassword({ email: 'b@c.com', password: pass73Char }, function(err) {
|
||||
assert(err);
|
||||
expect(err).to.match(/Password too long/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Access-hook for queries with email NOT case-sensitive', function() {
|
||||
it('Should not throw an error if the query does not contain {where: }', function(done) {
|
||||
User.find({}, function(err) {
|
||||
|
@ -678,6 +740,23 @@ describe('User', function() {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('allows login with password too long but created in old LB version',
|
||||
function(done) {
|
||||
var bcrypt = require('bcryptjs');
|
||||
var longPassword = new Array(80).join('a');
|
||||
var oldHash = bcrypt.hashSync(longPassword, bcrypt.genSaltSync(1));
|
||||
|
||||
User.create({ email: 'b@c.com', password: oldHash }, function(err) {
|
||||
if (err) return done(err);
|
||||
User.login({ email: 'b@c.com', password: longPassword }, function(err, accessToken) {
|
||||
if (err) return done(err);
|
||||
assert(accessToken.id);
|
||||
// we are logged in, the test passed
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function assertGoodToken(accessToken) {
|
||||
|
|
Loading…
Reference in New Issue