diff --git a/bcrypt.js b/bcrypt.js new file mode 100644 index 0000000000..a926310923 --- /dev/null +++ b/bcrypt.js @@ -0,0 +1,30 @@ + +'use strict'; + + +var bcrypt = require('bcryptjs'); + +process.on('message', function(m) { + if (m.type === 'hash') { + hash(m.rounds, m.password); + } else if (m.type === 'compare') { + compare(m.password, m.hash); + } +}); + +function hash(rounds, password) { + bcrypt.genSalt(rounds, function(err, salt) { + if (err) { + return process.send({type:'hash', err: {message: err.message}}); + } + bcrypt.hash(password, salt, function(err, hash) { + process.send({type:'hash', err: err ? {message: err.message} : null, hash: hash, password: password}); + }); + }); +} + +function compare(password, hash) { + bcrypt.compare(password, hash, function(err, res) { + process.send({type:'compare', err: err ? {message: err.message} : null, hash: hash, password: password, result: res}); + }); +} \ No newline at end of file diff --git a/src/password.js b/src/password.js new file mode 100644 index 0000000000..c4a7febca8 --- /dev/null +++ b/src/password.js @@ -0,0 +1,51 @@ + + +'use strict'; +var fork = require('child_process').fork; + +(function(module) { + + var child = fork('./bcrypt', process.argv.slice(2), { + env: process.env + }); + + var callbacks = { + 'hash': {}, + 'compare': {} + }; + + module.hash = function(rounds, password, callback) { + sendCommand({type: 'hash', password: password, rounds: rounds}, callback); + }; + + module.compare = function(password, hash, callback) { + sendCommand({type: 'compare', password: password, hash: hash}, callback); + }; + + function sendCommand(data, callback) { + callbacks[data.type][data.password] = callbacks[data.type][data.password] || []; + callbacks[data.type][data.password].push(callback); + child.send(data); + } + + child.on('message', function(msg) { + var cbs = callbacks[msg.type] ? callbacks[msg.type][msg.password] : null; + + if (Array.isArray(cbs)) { + if (msg.err) { + var err = new Error(msg.err.message); + cbs.forEach(function(callback) { + callback(err); + }); + cbs.length = 0; + return; + } + + cbs.forEach(function(callback) { + callback(null, msg.type === 'hash' ? msg.hash : msg.result); + }); + cbs.length = 0; + } + }); + +}(exports)); diff --git a/src/routes/authentication.js b/src/routes/authentication.js index 42139e87c4..19d13aeae6 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -4,15 +4,15 @@ var passport = require('passport'), passportLocal = require('passport-local').Strategy, nconf = require('nconf'), - bcrypt = require('bcryptjs'), + Password = require('../password'), winston = require('winston'), async = require('async'), - meta = require('./../meta'), - user = require('./../user'), - plugins = require('./../plugins'), + meta = require('../meta'), + user = require('../user'), + plugins = require('../plugins'), db = require('../database'), - utils = require('./../../public/src/utils'), + utils = require('../../public/src/utils'), login_strategies = []; @@ -226,7 +226,7 @@ return next(null, false, '[[error:user-banned]]'); } - bcrypt.compare(password, userData.password, function(err, res) { + Password.compare(password, userData.password, function(err, res) { if (err) { return next(new Error('bcrypt compare error')); } diff --git a/src/user.js b/src/user.js index c0e96960c6..951b47e8d9 100644 --- a/src/user.js +++ b/src/user.js @@ -1,6 +1,6 @@ 'use strict'; -var bcrypt = require('bcryptjs'), +var async = require('async'), nconf = require('nconf'), gravatar = require('gravatar'), @@ -9,7 +9,8 @@ var bcrypt = require('bcryptjs'), db = require('./database'), meta = require('./meta'), groups = require('./groups'), - emitter = require('./emitter'); + emitter = require('./emitter'), + Password = require('./password'); (function(User) { @@ -295,12 +296,7 @@ var bcrypt = require('bcryptjs'), return callback(null, password); } - bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) { - if (err) { - return callback(err); - } - bcrypt.hash(password, salt, callback); - }); + Password.hash(nconf.get('bcrypt_rounds'), password, callback); }; User.onNewPostMade = function(postData) { diff --git a/src/user/profile.js b/src/user/profile.js index ea332dee21..d76ac2bf65 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -1,15 +1,15 @@ 'use strict'; -var bcrypt = require('bcryptjs'), - async = require('async'), +var async = require('async'), validator = require('validator'), S = require('string'), - utils = require('./../../public/src/utils'), - meta = require('./../meta'), - events = require('./../events'), - db = require('./../database'); + utils = require('../../public/src/utils'), + meta = require('../meta'), + events = require('../events'), + db = require('../database'), + Password = require('../password'); module.exports = function(User) { @@ -248,7 +248,7 @@ module.exports = function(User) { return hashAndSetPassword(callback); } - bcrypt.compare(data.currentPassword, currentPassword, function(err, res) { + Password.compare(data.currentPassword, currentPassword, function(err, res) { if (err || !res) { return callback(err || new Error('[[user:change_password_error_wrong_current]]')); }