Add Insight save and register
This commit is contained in:
parent
6af23a995e
commit
5d980af518
9 changed files with 168 additions and 43 deletions
15
.jshint
Normal file
15
.jshint
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"freeze": true,
|
||||
"indent": 2,
|
||||
"newcap": true,
|
||||
"quotmark": "single",
|
||||
"maxdepth": 3,
|
||||
"maxstatements": 15,
|
||||
"maxlen": 80,
|
||||
"eqnull": true,
|
||||
"funcscope": true,
|
||||
"node": true
|
||||
}
|
||||
|
|
@ -3,15 +3,7 @@
|
|||
angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) {
|
||||
controllerUtils.redirIfLogged();
|
||||
|
||||
$scope.retreiving = true;
|
||||
copay.Identity.anyProfile({
|
||||
pluginManager: pluginManager,
|
||||
}, function(any) {
|
||||
$scope.retreiving = false;
|
||||
if (!any)
|
||||
$location.path('/createProfile');
|
||||
});
|
||||
|
||||
|
||||
$scope.openProfile = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ var preconditions = require('preconditions').singleton();
|
|||
var _ = require('underscore');
|
||||
var log = require('../log');
|
||||
|
||||
var querystring = require('querystring');
|
||||
var request = require('request');
|
||||
var cryptoUtil = require('../util/crypto');
|
||||
var version = require('../../version').version;
|
||||
var TxProposals = require('./TxProposals');
|
||||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
|
|
@ -27,6 +30,8 @@ var Storage = module.exports.Storage = require('./Storage');
|
|||
function Identity(password, opts) {
|
||||
preconditions.checkArgument(opts);
|
||||
|
||||
opts = _.extend({}, opts);
|
||||
this.request = opts.request || request;
|
||||
this.storage = Identity._getStorage(opts, password);
|
||||
this.networkOpts = {
|
||||
'livenet': opts.network.livenet,
|
||||
|
|
@ -37,6 +42,7 @@ function Identity(password, opts) {
|
|||
'testnet': opts.network.testnet,
|
||||
};
|
||||
|
||||
this.insightSaveOpts = opts.insightSave || {};
|
||||
this.walletDefaults = opts.walletDefaults || {};
|
||||
this.version = opts.version || version;
|
||||
|
||||
|
|
@ -50,8 +56,6 @@ Identity._createProfile = function(email, password, storage, cb) {
|
|||
Profile.create(email, password, storage, cb);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Identity._newStorage = function(opts) {
|
||||
return new Storage(opts);
|
||||
};
|
||||
|
|
@ -82,8 +86,6 @@ Identity._newAsync = function(opts) {
|
|||
return new Async(opts);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Identity._getStorage = function(opts, password) {
|
||||
var storageOpts = {};
|
||||
|
||||
|
|
@ -150,12 +152,18 @@ Identity.create = function(email, password, opts, cb) {
|
|||
requiredCopayers: 1,
|
||||
totalCopayers: 1,
|
||||
password: password,
|
||||
name: 'general',
|
||||
name: 'general'
|
||||
});
|
||||
iden.createWallet(wopts, function(err, w) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
iden.registerOnInsight(iden.insightSaveOpts, function(error) {
|
||||
// Ignore error
|
||||
return cb(null, iden, w);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -186,7 +194,12 @@ Identity.open = function(email, password, opts, cb) {
|
|||
var iden = new Identity(password, opts);
|
||||
|
||||
Identity._openProfile(email, password, iden.storage, function(err, profile) {
|
||||
if (err) return cb(err);
|
||||
if (err) {
|
||||
if (err.message && err.message.indexOf('PNOTFOUND') !== -1) {
|
||||
return Identity.readFromInsight(email, password, opts, cb);
|
||||
}
|
||||
return cb(err);
|
||||
}
|
||||
iden.profile = profile;
|
||||
|
||||
var wids = _.pluck(iden.listWallets(), 'id');
|
||||
|
|
@ -335,15 +348,14 @@ Identity.prototype.closeWallet = function(wid, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @desc Return a base64 encrypted version of the wallet
|
||||
* @return {string} base64 encoded string
|
||||
*/
|
||||
Identity.import = function(str, password, opts, cb) {
|
||||
Identity.importFromJson = function(str, password, opts, cb) {
|
||||
preconditions.checkArgument(str);
|
||||
var json = JSON.parse(str);
|
||||
var json;
|
||||
try {
|
||||
json = JSON.parse(str);
|
||||
} catch (e) {
|
||||
return cb('Unable to retrieve json from string', str);
|
||||
}
|
||||
|
||||
if (!_.isNumber(json.iterations))
|
||||
return cb('BADSTR: Missing iterations');
|
||||
|
|
@ -351,25 +363,34 @@ Identity.import = function(str, password, opts, cb) {
|
|||
if (!json.profile)
|
||||
return cb('BADSTR: Missing profile');
|
||||
|
||||
|
||||
var iden = new Identity(password, opts);
|
||||
iden.profile = Profile.import(json.profile, password, iden.storage);
|
||||
|
||||
json.wallets = json.wallets || {};
|
||||
var walletInfoBackup = iden.profile.walletInfos;
|
||||
iden.profile.walletInfos = {};
|
||||
|
||||
var l = json.wallets.length,
|
||||
var l = _.size(json.wallets),
|
||||
i = 0;
|
||||
|
||||
if (!l)
|
||||
return cb(null, iden);
|
||||
|
||||
_.each(this.wallets, function(wstr) {
|
||||
iden.importWallet(wstr, password, skipFields, function(err, w) {
|
||||
_.each(json.wallets, function(wstr) {
|
||||
iden.importWallet(wstr, password, opts.skipFields, function(err, w) {
|
||||
if (err) return cb(err);
|
||||
log.debug('Wallet ' + w.getId() + ' imported');
|
||||
|
||||
if (++i == l)
|
||||
iden.store(cb);
|
||||
if (++i == l) {
|
||||
iden.profile.walletInfos = walletInfoBackup;
|
||||
iden.store(opts, function(err) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
} else {
|
||||
return cb(null, iden, iden.openWallets[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
|
@ -378,7 +399,7 @@ Identity.import = function(str, password, opts, cb) {
|
|||
* @desc Return JSON with base64 encoded strings for wallets and profile, and iteration count
|
||||
* @return {string} Stringify JSON
|
||||
*/
|
||||
Identity.prototype.export = function() {
|
||||
Identity.prototype.exportAsJson = function() {
|
||||
var ret = {};
|
||||
ret.iterations = this.storage.iterations;
|
||||
ret.profile = this.profile.export();
|
||||
|
|
@ -470,8 +491,59 @@ Identity.prototype.createWallet = function(opts, cb) {
|
|||
this.addWallet(w, function(err) {
|
||||
if (err) return cb(err);
|
||||
self.openWallets.push(w);
|
||||
self.triggerInsightSave(self.insightSaveOpts, function(error) {
|
||||
// Ignore error
|
||||
w.netStart();
|
||||
return cb(err, w);
|
||||
return cb(null, w);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Identity.readFromInsight = function(email, password, opts, callback) {
|
||||
var key = cryptoUtil.kdf(password, email);
|
||||
var secret = cryptoUtil.kdf(key, password);
|
||||
var useRequest = opts.request || request;
|
||||
var encodedEmail = encodeURIComponent(email);
|
||||
var retrieveUrl = opts.retrieveUrl || 'http://localhost:3001/api/email/retrieve/' + encodedEmail;
|
||||
useRequest.get(retrieveUrl + '?' + querystring.encode({secret: secret}),
|
||||
function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||
if (!decryptedJson) {
|
||||
return callback('Internal Error');
|
||||
}
|
||||
return Identity.importFromJson(decryptedJson, password, opts, callback);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Identity.prototype.triggerInsightSave = Identity.prototype.registerOnInsight = function(opts, callback) {
|
||||
var password = this.profile.password;
|
||||
var key = cryptoUtil.kdf(password, this.profile.email);
|
||||
var secret = cryptoUtil.kdf(key, password);
|
||||
var exportData = this.exportAsJson
|
||||
var record = cryptoUtil.encrypt(key, this.exportAsJson());
|
||||
var registerUrl = opts.registerUrl || 'http://localhost:3001/api/email/register';
|
||||
this.request.post({
|
||||
url: registerUrl,
|
||||
body: querystring.encode({
|
||||
email: this.profile.email,
|
||||
secret: secret,
|
||||
record: record
|
||||
})
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
return callback('Unable to store data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -486,11 +558,8 @@ Identity.prototype.addWallet = function(wallet, cb) {
|
|||
self.profile.addWallet(wallet.getId(), {
|
||||
name: wallet.name
|
||||
}, function(err) {
|
||||
|
||||
if (err) return cb(err);
|
||||
wallet.store(function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ function Profile(info, storage) {
|
|||
preconditions.checkArgument(storage);
|
||||
preconditions.checkArgument(storage.setPassword, 'bad storage');
|
||||
|
||||
this.password = info.password;
|
||||
this.hash = info.hash;
|
||||
this.email = info.email;
|
||||
this.extra = info.extra || {};
|
||||
|
|
@ -19,8 +20,8 @@ function Profile(info, storage) {
|
|||
this.storage = storage;
|
||||
};
|
||||
|
||||
Profile.hash = function(email, password) {
|
||||
return bitcore.util.sha256ripe160(email + password).toString('hex');
|
||||
Profile.hash = function(email) {
|
||||
return bitcore.util.sha256ripe160(email).toString('hex');
|
||||
};
|
||||
|
||||
Profile.key = function(hash) {
|
||||
|
|
@ -36,6 +37,7 @@ Profile.create = function(email, password, storage, cb) {
|
|||
|
||||
var p = new Profile({
|
||||
email: email,
|
||||
password: password,
|
||||
hash: Profile.hash(email, password),
|
||||
}, storage);
|
||||
p.store({}, function(err) {
|
||||
|
|
@ -67,7 +69,7 @@ Profile.open = function(email, password, storage, cb) {
|
|||
};
|
||||
|
||||
Profile.prototype.toObj = function() {
|
||||
return _.clone(_.pick(this, 'hash', 'email', 'extra', 'walletInfos'));
|
||||
return _.clone(_.pick(this, 'password', 'hash', 'email', 'extra', 'walletInfos'));
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('pluginManager', function(angularLoad){
|
||||
angular.module('copayApp.services').factory('pluginManager', function(angularLoad) {
|
||||
var pm = new copay.PluginManager(config);
|
||||
var scripts = pm.scripts;
|
||||
|
||||
|
|
|
|||
44
js/util/crypto.js
Normal file
44
js/util/crypto.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Small module for some helpers that wrap CryptoJS with some good practices.
|
||||
*/
|
||||
var sjcl = require('sjcl');
|
||||
var log = require('../log.js');
|
||||
var _ = require('underscore');
|
||||
|
||||
var SALT = 'copay random string NWRlNmExMTE4NzIzYzYyYWMwODU1MTdkN';
|
||||
var SEPARATOR = '&';
|
||||
var defaultOptions = {
|
||||
adata: '',
|
||||
cipher: 'aes',
|
||||
ks: 128,
|
||||
iter: 2000,
|
||||
mode: 'ccm',
|
||||
ts: 64
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
kdf: function(value1, value2) {
|
||||
return sjcl.codec.base64.fromBits(sjcl.misc.pbkdf2(value1 + value2, SALT));
|
||||
},
|
||||
|
||||
/**
|
||||
* Encrypts symmetrically using a passphrase
|
||||
*/
|
||||
encrypt: function(key, message) {
|
||||
return sjcl.encrypt(key, message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts symmetrically using a passphrase
|
||||
*/
|
||||
decrypt: function(key, cypher) {
|
||||
var output = {};
|
||||
try {
|
||||
return sjcl.decrypt(key, cypher);
|
||||
} catch (e) {
|
||||
log.error('Decryption failed due to error: ' + e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
"inherits": "^2.0.1",
|
||||
"optimist": "^0.6.1",
|
||||
"preconditions": "^1.0.7",
|
||||
"querystring": "^0.2.0",
|
||||
"request": "^2.40.0",
|
||||
"underscore": "^1.7.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ function LocalStorage() {
|
|||
LocalStorage.prototype.init = function() {
|
||||
};
|
||||
|
||||
|
||||
LocalStorage.prototype.getItem = function(k,cb) {
|
||||
return cb(localStorage.getItem(k));
|
||||
};
|
||||
|
|
@ -37,5 +36,4 @@ LocalStorage.prototype.allKeys = function(cb) {
|
|||
return cb(ret);
|
||||
};
|
||||
|
||||
|
||||
module.exports = LocalStorage;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ var createBundle = function(opts) {
|
|||
expose: 'request'
|
||||
});
|
||||
b.require('underscore');
|
||||
b.require('querystring');
|
||||
b.require('assert');
|
||||
b.require('preconditions');
|
||||
|
||||
|
|
@ -86,6 +87,9 @@ var createBundle = function(opts) {
|
|||
b.require('./js/models/PluginManager', {
|
||||
expose: '../js/models/PluginManager'
|
||||
});
|
||||
b.require('./js/util/crypto', {
|
||||
expose: '../util/crypto'
|
||||
});
|
||||
|
||||
if (!opts.disablePlugins) {
|
||||
b.require('./plugins/GoogleDrive', {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue