From 46875ccddd90d4678cb7f7512d6d1d7581e7b77f Mon Sep 17 00:00:00 2001 From: NuSkooler Date: Sat, 25 Oct 2014 21:35:42 -0600 Subject: [PATCH] * User stuff converted to use Async.js. More to come --- core/edit_text_view.js | 2 + core/user.js | 272 ++++++++++++++++++++++++++++++++--------- mods/matrix.js | 6 +- 3 files changed, 223 insertions(+), 57 deletions(-) diff --git a/core/edit_text_view.js b/core/edit_text_view.js index c24c0290..43440bd8 100644 --- a/core/edit_text_view.js +++ b/core/edit_text_view.js @@ -29,6 +29,8 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) { assert(1 === key.length); + console.log('key: ' + key); + if(this.text.length < this.options.maxLength) { key = strUtil.stylizeString(key, this.textStyle); diff --git a/core/user.js b/core/user.js index 324c7878..d292d43f 100644 --- a/core/user.js +++ b/core/user.js @@ -4,12 +4,13 @@ var userDb = require('./database.js').dbs.user; var crypto = require('crypto'); var assert = require('assert'); +var async = require('async'); exports.User = User; exports.getUserId = getUserId; exports.createNew = createNew; -exports.generatePasswordDerivedKey = generatePasswordDerivedKey; exports.persistAll = persistAll; +exports.authenticate = authenticate; function User() { var self = this; @@ -47,6 +48,10 @@ User.PBKDF2 = { saltLen : 32, }; +User.StandardPropertyGroups = { + password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ], +}; + function getUserId(userName, cb) { userDb.get( 'SELECT id ' + @@ -54,7 +59,15 @@ function getUserId(userName, cb) { 'WHERE user_name LIKE ?;', [ userName ], function onResults(err, row) { - cb(err, row.id); + if(err) { + cb(err); + } else { + if(row) { + cb(null, row.id); + } else { + cb(new Error('No matching user name')); + } + } } ); } @@ -62,58 +75,107 @@ function getUserId(userName, cb) { function createNew(user, cb) { assert(user.userName && user.userName.length > 1, 'Invalid userName'); - userDb.run( - 'INSERT INTO user (user_name) ' + - 'VALUES (?);', - [ user.userName ], - function onUserInsert(err) { - if(err) { - cb(err); - } else { - user.id = this.lastID; - - // - // Allow converting user.password -> Salt/DK - // + async.series( + [ + function beginTransaction(callback) { + userDb.run('BEGIN;', function onBegin(err) { + callback(err); + }); + }, + function createUserRec(callback) { + userDb.run( + 'INSERT INTO user (user_name) ' + + 'VALUES (?);', + [ user.userName ], + function onUserInsert(err) { + if(err) { + callback(err); + } else { + user.id = this.lastID; + callback(null); + } + } + ); + }, + function genPasswordDkAndSaltIfRequired(callback) { if(user.password && user.password.length > 0) { - generatePasswordDerivedKey(user.password, function onDkGenerated(err, dk) { - user.properties = user.properties || { - pw_pbkdf2_salt : dk.salt, - pw_pbkdf2_dk : dk.dk, - }; - - persistAll(user, function onUserPersisted() { - cb(null, user.id); - }); - }); + generatePasswordDerivedKeyAndSalt(user.password, function onDkAndSalt(err, info) { + if(err) { + callback(err); + } else { + user.properties = user.properties || {}; + user.properties.pw_pbkdf2_salt = info.salt; + user.properties.pw_pbkdf2_dk = info.dk; + callback(null); + } + }); } else { - persistAll(user, function onUserPersisted() { - cb(null, user.id); - }); + callback(null); } + }, + function saveAll(callback) { + persistAll(user, false, function onPersisted(err) { + callback(err); + }); + } + ], + function onComplete(err) { + if(err) { + userDb.run('ROLLBACK;', function onRollback(err) { + cb(err); + }); + } else { + userDb.run('COMMIT;', function onCommit(err) { + + if(err) { + cb(err); + } else { + cb(null, user.id); + } + }); } } ); } -function generatePasswordDerivedKey(password, cb) { - crypto.randomBytes(User.PBKDF2.saltLen, function onRandomSalt(err, salt) { +function generatePasswordDerivedKeyAndSalt(password, cb) { + async.waterfall( + [ + function getSalt(callback) { + generatePasswordDerivedKeySalt(function onSalt(err, salt) { + callback(err, salt); + }); + }, + function getDk(salt, callback) { + generatePasswordDerivedKey(password, salt, function onDk(err, dk) { + callback(err, salt, dk); + }); + } + ], + function onComplete(err, salt, dk) { + cb(err, { salt : salt, dk : dk }); + } + ); +} + +function generatePasswordDerivedKeySalt(cb) { + crypto.randomBytes(User.PBKDF2.saltLen, function onRandSalt(err, salt) { if(err) { cb(err); - return; + } else { + cb(null, salt.toString('hex')); } + }); +} - salt = salt.toString('hex'); - - password = new Buffer(password).toString('hex'); - - crypto.pbkdf2(password, salt, User.PBKDF2.iterations, User.PBKDF2.keyLen, function onDerivedKey(err, dk) { - if(err) { - cb(err); - } else { - cb(null, { dk : dk.toString('hex'), salt : salt } ); - } - }); +function generatePasswordDerivedKey(password, salt, cb) { + password = new Buffer(password).toString('hex'); + crypto.pbkdf2(password, salt, User.PBKDF2.iterations, User.PBKDF2.keyLen, function onDerivedKey(err, dk) { + if(err) { + cb(err); + } else { + cb(null, dk.toString('hex')); + } }); } @@ -124,27 +186,125 @@ function persistProperties(user, cb) { 'REPLACE INTO user_property (user_id, prop_name, prop_value) ' + 'VALUES (?, ?, ?);'); - Object.keys(user.properties).forEach(function onProp(name) { - stmt.run(user.id, name, user.properties[name]); - }); - - stmt.finalize(function onFinalized() { - if(cb) { - cb(); + async.each(Object.keys(user.properties), function onProp(propName, callback) { + stmt.run(user.id, propName, user.properties[propName], function onRun(err) { + callback(err); + }); + }, function onComplete(err) { + if(err) { + cb(err); + } else { + stmt.finalize(function onFinalized() { + cb(null); + }); } }); } -function persistAll(user, cb) { +function getProperties(userId, propNames, cb) { + var properties = {}; + + async.each(propNames, function onPropName(propName, next) { + userDb.get( + 'SELECT prop_value ' + + 'FROM user_property ' + + 'WHERE user_id = ? AND prop_name = ?;', + [ userId, propName ], + function onRow(err, row) { + if(err) { + next(err); + } else { + if(row) { + properties[propName] = row.prop_value; + next(); + } else { + next(new Error('No property "' + propName + '" for user ' + userId)); + } + } + } + ); + }, function onCompleteOrError(err) { + if(err) { + cb(err); + } else { + cb(null, properties); + } + }); +} + +function persistAll(user, useTransaction, cb) { assert(user.id > 0); - userDb.serialize(function onSerialized() { - userDb.run('BEGIN;'); + async.series( + [ + function beginTransaction(callback) { + if(useTransaction) { + userDb.run('BEGIN;', function onBegin(err) { + callback(err); + }); + } else { + callback(null); + } + }, + function saveProps(callback) { + persistProperties(user, function onPropPersist(err) { + callback(err); + }); + } + ], + function onComplete(err) { + if(err) { + if(useTransaction) { + userDb.run('ROLLBACK;', function onRollback(err) { + cb(err); + }); + } else { + cb(err); + } + } else { + if(useTransaction) { + userDb.run('COMMIT;', function onCommit(err) { + cb(err); + }); + } else { + cb(null); + } + } + } + ); +} - persistProperties(user); +function authenticate(userName, password, client, cb) { + assert(client); - userDb.run('COMMIT;'); - }); + async.waterfall( + [ + function fetchUserId(callback) { + // get user ID + getUserId(userName, function onUserId(err, userId) { + callback(err, userId); + }); + }, - cb(); + function getRequiredAuthProperties(userId, callback) { + // fetch properties required for authentication + getProperties(userId, User.StandardPropertyGroups.password, function onProps(err, props) { + callback(err, props); + }); + }, + function getDkWithSalt(props, callback) { + // get DK from stored salt and password provided + generatePasswordDerivedKey(password, props.pw_pbkdf2_salt, function onDk(err, dk) { + callback(err, dk, props.pw_pbkdf2_dk); + }); + } + ], + function validateAuth(err, passDk, propsDk) { + if(err) { + cb(false); + } else { + cb(passDk === propsDk); + } + } + ); } \ No newline at end of file diff --git a/mods/matrix.js b/mods/matrix.js index 48a3adff..7e425f78 100644 --- a/mods/matrix.js +++ b/mods/matrix.js @@ -60,8 +60,12 @@ function entryPoint(client) { etv.redraw();*/ + user.authenticate('NuSkooler', 'password', client, function onAuth(isValid) { + console.log(isValid); + }); + user.createNew({ - userName : 'Scooby', + userName : 'NuSkooler', password : 'password', //properties : { // pw_pbkdf2_salt : '81b45dc699c716ac1913039138b64e3057844128cf1f9291c6475d26dab3d4a5',