commit
21bdb28d37
|
@ -14,7 +14,7 @@ var utils = require('../../lib/utils');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var SALT_WORK_FACTOR = 10;
|
var SALT_WORK_FACTOR = 10;
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var MAX_PASSWORD_LENGTH = 72;
|
||||||
var bcrypt;
|
var bcrypt;
|
||||||
try {
|
try {
|
||||||
// Try the native module first
|
// Try the native module first
|
||||||
|
@ -548,7 +548,6 @@ module.exports = function(User) {
|
||||||
cb = cb || utils.createPromiseCallback();
|
cb = cb || utils.createPromiseCallback();
|
||||||
var UserModel = this;
|
var UserModel = this;
|
||||||
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
|
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (typeof options.email !== 'string') {
|
if (typeof options.email !== 'string') {
|
||||||
var err = new Error(g.f('Email is required'));
|
var err = new Error(g.f('Email is required'));
|
||||||
|
@ -558,6 +557,13 @@ module.exports = function(User) {
|
||||||
return cb.promise;
|
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) {
|
UserModel.findOne({ where: { email: options.email }}, function(err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
|
@ -596,14 +602,20 @@ module.exports = function(User) {
|
||||||
};
|
};
|
||||||
|
|
||||||
User.validatePassword = function(plain) {
|
User.validatePassword = function(plain) {
|
||||||
if (typeof plain === 'string' && plain) {
|
var err;
|
||||||
|
if (plain && typeof plain === 'string' && plain.length <= MAX_PASSWORD_LENGTH) {
|
||||||
return true;
|
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;
|
err.statusCode = 422;
|
||||||
throw err;
|
throw err;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Setup an extended user model.
|
* Setup an extended user model.
|
||||||
*/
|
*/
|
||||||
|
|
2
index.js
2
index.js
|
@ -4,7 +4,7 @@
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
var SG = require('strong-globalize');
|
var SG = require('strong-globalize');
|
||||||
SG.SetRootDir(__dirname, {autonomousMsgLoading: 'all'});
|
SG.SetRootDir(__dirname, { autonomousMsgLoading: 'all' });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loopback ~ public api
|
* 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() {
|
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) {
|
it('Should not throw an error if the query does not contain {where: }', function(done) {
|
||||||
User.find({}, function(err) {
|
User.find({}, function(err) {
|
||||||
|
@ -678,6 +740,23 @@ describe('User', function() {
|
||||||
done();
|
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) {
|
function assertGoodToken(accessToken) {
|
||||||
|
|
Loading…
Reference in New Issue