mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
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:
@@ -115,17 +115,15 @@ api.registerLocal = {
|
||||
if (lowerCaseUsername === user.auth.local.lowerCaseUsername) throw new NotAuthorized(res.t('usernameTaken'));
|
||||
}
|
||||
|
||||
let salt = passwordUtils.makeSalt();
|
||||
let hashed_password = passwordUtils.encrypt(password, salt); // eslint-disable-line camelcase
|
||||
let hashed_password = await passwordUtils.bcryptHash(password); // eslint-disable-line camelcase
|
||||
let newUser = {
|
||||
auth: {
|
||||
local: {
|
||||
username,
|
||||
lowerCaseUsername,
|
||||
email,
|
||||
salt,
|
||||
hashed_password, // eslint-disable-line camelcase
|
||||
passwordHashMethod: 'sha1',
|
||||
hashed_password, // eslint-disable-line camelcase,
|
||||
passwordHashMethod: 'bcrypt',
|
||||
},
|
||||
},
|
||||
preferences: {
|
||||
@@ -223,6 +221,7 @@ api.loginLocal = {
|
||||
|
||||
let login;
|
||||
let username = req.body.username;
|
||||
let password = req.body.password;
|
||||
|
||||
if (validator.isEmail(username)) {
|
||||
login = {'auth.local.email': username.toLowerCase()}; // Emails are stored lowercase
|
||||
@@ -230,10 +229,25 @@ api.loginLocal = {
|
||||
login = {'auth.local.username': username};
|
||||
}
|
||||
|
||||
let user = await User.findOne(login, {auth: 1, apiToken: 1}).exec();
|
||||
let isValidPassword = user && user.auth.local.hashed_password === passwordUtils.encrypt(req.body.password, user.auth.local.salt);
|
||||
// load the entire user because we may have to save it to convert the password to bcrypt
|
||||
let user = await User.findOne(login).exec();
|
||||
|
||||
let isValidPassword;
|
||||
|
||||
if (!user) {
|
||||
isValidPassword = false;
|
||||
} else {
|
||||
isValidPassword = await passwordUtils.compare(user, password);
|
||||
}
|
||||
|
||||
if (!isValidPassword) throw new NotAuthorized(res.t('invalidLoginCredentialsLong'));
|
||||
|
||||
// convert the hashed password to bcrypt from sha1
|
||||
if (user.auth.local.passwordHashMethod === 'sha1') {
|
||||
await passwordUtils.convertToBcrypt(user, password);
|
||||
await user.save();
|
||||
}
|
||||
|
||||
res.analytics.track('login', {
|
||||
category: 'behaviour',
|
||||
type: 'local',
|
||||
@@ -433,12 +447,18 @@ api.updateUsername = {
|
||||
|
||||
if (!user.auth.local.username) throw new BadRequest(res.t('userHasNoLocalRegistration'));
|
||||
|
||||
let oldPassword = passwordUtils.encrypt(req.body.password, user.auth.local.salt);
|
||||
if (oldPassword !== user.auth.local.hashed_password) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
let password = req.body.password;
|
||||
let isValidPassword = await passwordUtils.compare(user, password);
|
||||
if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
|
||||
let count = await User.count({ 'auth.local.lowerCaseUsername': req.body.username.toLowerCase() });
|
||||
if (count > 0) throw new BadRequest(res.t('usernameTaken'));
|
||||
|
||||
// if password is using old sha1 encryption, change it
|
||||
if (user.auth.local.passwordHashMethod === 'sha1') {
|
||||
await passwordUtils.convertToBcrypt(user, password); // user is saved a few lines below
|
||||
}
|
||||
|
||||
// save username
|
||||
user.auth.local.lowerCaseUsername = req.body.username.toLowerCase();
|
||||
user.auth.local.username = req.body.username;
|
||||
@@ -487,13 +507,17 @@ api.updatePassword = {
|
||||
throw validationErrors;
|
||||
}
|
||||
|
||||
let oldPassword = passwordUtils.encrypt(req.body.password, user.auth.local.salt);
|
||||
if (oldPassword !== user.auth.local.hashed_password) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
let oldPassword = req.body.password;
|
||||
let isValidPassword = await passwordUtils.compare(user, oldPassword);
|
||||
if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
|
||||
if (req.body.newPassword !== req.body.confirmPassword) throw new NotAuthorized(res.t('passwordConfirmationMatch'));
|
||||
let newPassword = req.body.newPassword;
|
||||
if (newPassword !== req.body.confirmPassword) throw new NotAuthorized(res.t('passwordConfirmationMatch'));
|
||||
|
||||
user.auth.local.hashed_password = passwordUtils.encrypt(req.body.newPassword, user.auth.local.salt); // eslint-disable-line camelcase
|
||||
// set new password and make sure it's using bcrypt for hashing
|
||||
await passwordUtils.convertToBcrypt(user, newPassword);
|
||||
await user.save();
|
||||
|
||||
res.respond(200, {});
|
||||
},
|
||||
};
|
||||
@@ -522,15 +546,15 @@ api.resetPassword = {
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let email = req.body.email.toLowerCase();
|
||||
let salt = passwordUtils.makeSalt();
|
||||
let newPassword = passwordUtils.makeSalt(); // use a salt as the new password too (they'll change it later)
|
||||
let hashedPassword = passwordUtils.encrypt(newPassword, salt);
|
||||
|
||||
let user = await User.findOne({ 'auth.local.email': email }).exec();
|
||||
|
||||
if (user) {
|
||||
user.auth.local.salt = salt;
|
||||
user.auth.local.hashed_password = hashedPassword; // eslint-disable-line camelcase
|
||||
// use a salt as the new password too (they'll change it later)
|
||||
let newPassword = passwordUtils.sha1MakeSalt();
|
||||
|
||||
// set new password and make sure it's using bcrypt for hashing
|
||||
await passwordUtils.convertToBcrypt(user, newPassword); // user is saved a few lines below
|
||||
|
||||
sendEmail({
|
||||
from: 'Habitica <admin@habitica.com>',
|
||||
to: email,
|
||||
@@ -585,8 +609,14 @@ api.updateEmail = {
|
||||
|
||||
if (emailAlreadyInUse) throw new NotAuthorized(res.t('cannotFulfillReq'));
|
||||
|
||||
let candidatePassword = passwordUtils.encrypt(req.body.password, user.auth.local.salt);
|
||||
if (candidatePassword !== user.auth.local.hashed_password) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
let password = req.body.password;
|
||||
let isValidPassword = await passwordUtils.compare(user, password);
|
||||
if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
|
||||
// if password is using old sha1 encryption, change it
|
||||
if (user.auth.local.passwordHashMethod === 'sha1') {
|
||||
await passwordUtils.convertToBcrypt(user, password);
|
||||
}
|
||||
|
||||
user.auth.local.email = req.body.newEmail;
|
||||
await user.save();
|
||||
|
||||
@@ -200,8 +200,9 @@ api.deleteUser = {
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let oldPassword = passwordUtils.encrypt(req.body.password, user.auth.local.salt);
|
||||
if (oldPassword !== user.auth.local.hashed_password) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
let password = req.body.password;
|
||||
let isValidPassword = await passwordUtils.compare(user, password);
|
||||
if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword'));
|
||||
|
||||
if (plan && plan.customerId && !plan.dateTerminated) {
|
||||
throw new NotAuthorized(res.t('cannotDeleteActiveAccount'));
|
||||
@@ -256,6 +257,7 @@ api.getUserAnonymized = {
|
||||
if (user.auth) {
|
||||
delete user.auth.local;
|
||||
delete user.auth.facebook;
|
||||
delete user.auth.google;
|
||||
}
|
||||
delete user.newMessages;
|
||||
delete user.profile;
|
||||
|
||||
Reference in New Issue
Block a user