Migrate to bcrypt (#8446)

* start migrating to bcrypt

* added method to convert the password to bcrypt when logging in, added method to compare password without knowing the hashing algorhytm, remove default

* travis: try to upgrade to container based infrastructure

* travis: add deps to build bcrypt.js

* travis: add deps to build bcrypt.js

* travis: add deps to build bcrypt.js

* travis: add deps to build bcrypt.js

* use bcryptjs until bcrypt can be installed on travis, see https://github.com/kelektiv/node.bcrypt.js/issues/476

* correct sha1 unit tests

* try different mongodb repo

* try without mognodb services

* try again with bcrypt

* disable request logging in travis

* migrate missing routes

* simplify code

* remove bcryptjs

* fix typo

* fix typo

* fix typo in comment

* add unit tests for new passwords utility emthods

* travis: back to old infrastructure, containers often have timeouts

* add integration test for passwordHashMethod

* update shrinkwrap

* clarify code and add comments

* add integration tests

* fix linting

* fix integration tests
This commit is contained in:
Matteo Pagliazzi
2017-01-24 12:28:42 +01:00
committed by GitHub
parent 04f4eb8490
commit acad3b8873
16 changed files with 1439 additions and 722 deletions

View File

@@ -1,41 +1,240 @@
/* eslint-disable camelcase */
import {
encrypt as encryptPassword,
makeSalt,
sha1Encrypt as sha1EncryptPassword,
sha1MakeSalt,
bcryptHash,
bcryptCompare,
compare,
convertToBcrypt,
} from '../../../../../website/server/libs/password';
describe('Password Utilities', () => {
describe('Encrypt', () => {
it('always encrypt the same password to the same value when using the same salt', () => {
describe('compare', () => {
it('can compare a correct password hashed with SHA1', async () => {
let textPassword = 'mySecretPassword';
let salt = makeSalt();
let encryptedPassword = encryptPassword(textPassword, salt);
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
expect(encryptPassword(textPassword, salt)).to.eql(encryptedPassword);
let user = {
auth: {
local: {
hashed_password: hashedPassword,
salt,
passwordHashMethod: 'sha1',
},
},
};
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
it('never encrypt the same password to the same value when using a different salt', () => {
it('can compare an invalid password hashed with SHA1', async () => {
let textPassword = 'mySecretPassword';
let aSalt = makeSalt();
let anotherSalt = makeSalt();
let anEncryptedPassword = encryptPassword(textPassword, aSalt);
let anotherEncryptedPassword = encryptPassword(textPassword, anotherSalt);
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
let user = {
auth: {
local: {
hashed_password: hashedPassword,
salt,
passwordHashMethod: 'sha1',
},
},
};
let isValidPassword = await compare(user, 'wrongPassword');
expect(isValidPassword).to.eql(false);
});
it('can compare a correct password hashed with bcrypt', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let user = {
auth: {
local: {
hashed_password: hashedPassword,
passwordHashMethod: 'bcrypt',
},
},
};
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
it('can compare an invalid password hashed with bcrypt', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let user = {
auth: {
local: {
hashed_password: hashedPassword,
passwordHashMethod: 'bcrypt',
},
},
};
let isValidPassword = await compare(user, 'wrongPassword');
expect(isValidPassword).to.eql(false);
});
it('throws an error if user is missing', async () => {
try {
await compare(null, 'some password');
} catch (e) {
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
}
});
it('throws an error if passwordToCheck is missing', async () => {
try {
await compare({a: true});
} catch (e) {
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
}
});
it('throws an error if an invalid hashing method is used', async () => {
try {
await compare({
auth: {
local: {
passwordHashMethod: 'invalid',
},
},
}, 'pass');
} catch (e) {
expect(e.toString()).to.equal('Error: Invalid password hash method.');
}
});
it('returns true if comparing the same password', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
expect(isValidPassword).to.eql(true);
});
it('returns true if comparing a different password', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
expect(isValidPassword).to.eql(false);
});
});
describe('Make Salt', () => {
it('creates a salt with length 10 by default', () => {
let salt = makeSalt();
describe('convertToBcrypt', () => {
it('converts an user password hashed with sha1 to bcrypt', async () => {
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
expect(salt.length).to.eql(10);
let user = {
auth: {
local: {
hashed_password: hashedPassword,
salt,
passwordHashMethod: 'sha1',
},
},
};
await convertToBcrypt(user, textPassword);
expect(user.auth.local.salt).to.be.undefined;
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
expect(user.auth.local.hashed_password).to.be.a.string;
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
it('can create a salt of any length', () => {
let length = 24;
let salt = makeSalt(length);
it('throws an error if user is missing', async () => {
try {
await convertToBcrypt(null, 'string');
} catch (e) {
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
}
});
expect(salt.length).to.eql(length);
it('throws an error if plainTextPassword is missing', async () => {
try {
await convertToBcrypt({a: true});
} catch (e) {
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
}
});
});
describe('bcrypt', () => {
describe('Hash', () => {
it('returns a hashed string', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
expect(hashedPassword).to.be.a.string;
});
});
describe('Compare', () => {
it('returns true if comparing the same password', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
expect(isValidPassword).to.eql(true);
});
it('returns true if comparing a different password', async () => {
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
expect(isValidPassword).to.eql(false);
});
});
});
describe('SHA1', () => {
describe('Encrypt', () => {
it('always encrypt the same password to the same value when using the same salt', () => {
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let encryptedPassword = sha1EncryptPassword(textPassword, salt);
expect(sha1EncryptPassword(textPassword, salt)).to.eql(encryptedPassword);
});
it('never encrypt the same password to the same value when using a different salt', () => {
let textPassword = 'mySecretPassword';
let aSalt = sha1MakeSalt();
let anotherSalt = sha1MakeSalt();
let anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
let anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
});
});
describe('Make Salt', () => {
it('creates a salt with length 10 by default', () => {
let salt = sha1MakeSalt();
expect(salt.length).to.eql(10);
});
it('can create a salt of any length', () => {
let length = 24;
let salt = sha1MakeSalt(length);
expect(salt.length).to.eql(length);
});
});
});
});