diff --git a/config.js b/config.js index b1d2b87ab..3ac558850 100644 --- a/config.js +++ b/config.js @@ -56,12 +56,17 @@ var defaultConfig = { plugins: { LocalStorage: true, //GoogleDrive: true, + InsightStorage: true + }, + + InsightStorage: { + url: 'https://test-insight.bitpay.com:443/api/email' }, GoogleDrive: { home: 'copay', - /* + /* * This clientId was generated at: * https://console.developers.google.com/project * To run Copay with Google Drive at your domain you need diff --git a/js/models/Identity.js b/js/models/Identity.js index 68ad4ebe5..6f65cdc8c 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -4,9 +4,6 @@ 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'); @@ -31,7 +28,6 @@ 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, @@ -42,6 +38,7 @@ function Identity(password, opts) { 'testnet': opts.network.testnet, }; + this.pluginManager = opts.pluginManager || {}; this.insightSaveOpts = opts.insightSave || {}; this.walletDefaults = opts.walletDefaults || {}; this.version = opts.version || version; @@ -158,15 +155,19 @@ Identity.create = function(email, password, opts, cb) { if (err) { return cb(err); } - iden.registerOnInsight(iden.insightSaveOpts, function(error) { - // Ignore error - return cb(null, iden, w); - }); + iden.pluginManager.get('remote-backup').store( + iden, + iden.insightSaveOpts, + function(error) { + // FIXME: Ignoring this error may not be the best thing to do. But remote storage + // is not required for the user to use the wallet. + return cb(null, iden, w); + } + ); }); }); }; - /** * validates Profile's email * @@ -196,7 +197,7 @@ Identity.open = function(email, password, opts, cb) { Identity._openProfile(email, password, iden.storage, function(err, profile) { if (err) { if (err.message && err.message.indexOf('PNOTFOUND') !== -1) { - return Identity.readFromInsight(email, password, opts, cb); + return opts.pluginManager.get('remote-backup').retrieve(email, password, opts, cb); } return cb(err); } @@ -491,7 +492,7 @@ 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) { + self.pluginManager.get('remote-backup').store(self, self.insightSaveOpts, function(error) { // Ignore error w.netStart(); return cb(null, w); @@ -499,53 +500,6 @@ Identity.prototype.createWallet = function(opts, cb) { }); }; -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(); - }); -}; // add wallet (import) Identity.prototype.addWallet = function(wallet, cb) { diff --git a/js/models/PluginManager.js b/js/models/PluginManager.js index 2a2788b61..50e720e18 100644 --- a/js/models/PluginManager.js +++ b/js/models/PluginManager.js @@ -30,6 +30,7 @@ var KIND_MULTIPLE = PluginManager.KIND_MULTIPLE = 2; PluginManager.TYPE = {}; PluginManager.TYPE['DB'] = KIND_UNIQUE; +PluginManager.TYPE['remote-backup'] = KIND_UNIQUE; PluginManager.prototype._register = function(obj, name) { preconditions.checkArgument(obj.type, 'Plugin has not type:' + name); diff --git a/plugins/InsightStorage.js b/plugins/InsightStorage.js new file mode 100644 index 000000000..5c0295462 --- /dev/null +++ b/plugins/InsightStorage.js @@ -0,0 +1,60 @@ +var request = require('request'); +var cryptoUtil = require('../js/util/crypto'); +var querystring = require('querystring'); +var Identity = require('../js/models/Identity'); + +function InsightStorage(config) { + this.type = 'remote-backup'; + this.storeUrl = config.url || 'https://insight.is/api/email'; + this.request = config.request || request; +} + +InsightStorage.prototype.init = function () {}; + +InsightStorage.prototype.retrieve = function(email, password, opts, callback) { + var key = cryptoUtil.kdf(password, email); + var secret = cryptoUtil.kdf(key, password); + var encodedEmail = encodeURIComponent(email); + var retrieveUrl = this.storeUrl + '/retrieve/' + encodedEmail; + this.request.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); + } + ); +}; + +InsightStorage.prototype.store = function(identity, opts, callback) { + var password = identity.profile.password; + var key = cryptoUtil.kdf(password, identity.profile.email); + var secret = cryptoUtil.kdf(key, password); + var record = cryptoUtil.encrypt(key, identity.exportAsJson()); + var registerUrl = this.storeUrl + '/register'; + this.request.post({ + url: registerUrl, + body: querystring.encode({ + email: identity.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(); + }); +}; + +module.exports = InsightStorage; diff --git a/util/build.js b/util/build.js index 267396631..5dc7abc33 100644 --- a/util/build.js +++ b/util/build.js @@ -95,6 +95,9 @@ var createBundle = function(opts) { b.require('./plugins/GoogleDrive', { expose: '../plugins/GoogleDrive' }); + b.require('./plugins/InsightStorage', { + expose: '../plugins/InsightStorage' + }); b.require('./plugins/LocalStorage', { expose: '../plugins/LocalStorage' });