From 4109d4743f184b6e865615f735a4f3fdaff5ff26 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 09:47:02 +1200 Subject: [PATCH 1/6] Loading profiles from all sources, in preparation for merge. --- src/js/models/profile.js | 7 +++ src/js/services/storageService.js | 87 +++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/js/models/profile.js b/src/js/models/profile.js index 74b0c33b9..d80087e7f 100644 --- a/src/js/models/profile.js +++ b/src/js/models/profile.js @@ -62,6 +62,13 @@ Profile.prototype.isDeviceChecked = function(ua) { return this.checkedUA == ua; }; +/** + * + * @param {Profile} other + */ +Profile.prototype.merge = function(other) { + throw 'Profile merge not implemented.'; +}; Profile.prototype.setChecked = function(ua, walletId) { if (this.checkedUA != ua) { diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 7c4ad0a60..75d34fe1a 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -123,9 +123,89 @@ angular.module('copayApp.services') secureStorageService.set('profile', profile.toObj(), cb); }; - root.getProfile = function(cb) { - secureStorageService.get('profile', function(err, str) { + /** + * @callback getProfileCallback + * @param {Error} error - falsy if profile not found. + * @param {Profile} profile - falsy if error or profile not found. + */ + /** + * + * @param {Profile} oldProfile + * @param {Profile} secureProfile - may be falsy if no secure profile found. + * @param {getProfileCallback} cb + */ + function _migrateProfiles(oldProfile, secureProfile, cb) { + if (secureProfile) { + + } else { + root.storeNewProfile(oldProfile, function(err) { + if (err) { + cb(err, null); + return; + } + + return; + }); + } + + }; + + /** + * + * @param {getProfileCallback} cb + */ + root.getProfile = function(cb) { + secureStorageService.get('profile', function(secureErr, secureStr) { + var secureProfile; + var oldProfile; + + if (secureErr) { + return cb(secureErr); + } + + if (secureStr) { + try { + secureProfile = Profile.fromString(secureStr); + } catch (e) { + var profileError = new Error('Could not read secure profile.'); + return cb(profileError, null); + } + } + + storage.get('profile', function(getErr, str) { + if (getErr) { + return cb(getErr); + } + + if (!str) { + if (secureProfile) { + return cb(null, secureProfile); + } else { + return cb(null, null); + } + } + + decryptOnMobile(getStr, function(err, str) { + if (err) return cb(err); + var p, err; + try { + oldProfile = Profile.fromString(str); + } catch (e) { + $log.debug('Could not read profile:', e); + err = new Error('Could not read profile.'); + return(err, null); + } + + // Now we have to do a migration + _migrateProfiles(oldProfile, secureProfile, cb); + + }); + }); + }); + }; + + /* if (err || !str) return cb(err); @@ -140,8 +220,7 @@ angular.module('copayApp.services') } return cb(err, p); }); - }); - }; + */ root.setFeedbackInfo = function(feedbackValues, cb) { storage.set('feedback', feedbackValues, cb); From 3aa9bef8036609e934eeaf69bf374c2ddd090e72 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 14:37:54 +1200 Subject: [PATCH 2/6] The localStorageService now returns errors when they happen. --- src/js/services/localStorage.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/js/services/localStorage.js b/src/js/services/localStorage.js index c772b7eef..ba0db231b 100644 --- a/src/js/services/localStorage.js +++ b/src/js/services/localStorage.js @@ -20,8 +20,7 @@ angular.module('copayApp.services') if (isChromeApp || isNW) { chrome.storage.local.get(k, function(data) { - //TODO check for errors - return cb(null, data[k]); + return cb(chrome.runtime.lastError, data[k]); }); } else { return cb(null, ls.getItem(k)); @@ -56,16 +55,24 @@ angular.module('copayApp.services') obj[k] = v; - chrome.storage.local.set(obj, cb); + chrome.storage.local.set(obj, function(){ + cb(chrome.runtime.lastError); + }); } else { - ls.setItem(k, v); + try { + ls.setItem(k, v); + } catch (e) { + return cb(e); + } return cb(); } }; root.remove = function(k, cb) { if (isChromeApp || isNW) { - chrome.storage.local.remove(k, cb); + chrome.storage.local.remove(k, function(){ + cb(chrome.runtime.lastError); + }); } else { ls.removeItem(k); return cb(); From adf7115c9cc337dd48d0358e8634ad7731ea7ce3 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 14:38:15 +1200 Subject: [PATCH 3/6] Merging profiles. --- src/js/models/profile.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/js/models/profile.js b/src/js/models/profile.js index d80087e7f..ac4b4828c 100644 --- a/src/js/models/profile.js +++ b/src/js/models/profile.js @@ -67,7 +67,22 @@ Profile.prototype.isDeviceChecked = function(ua) { * @param {Profile} other */ Profile.prototype.merge = function(other) { - throw 'Profile merge not implemented.'; + + var newCredentials = []; + + other.credentials.forEach(function(otherCredential) { + var credentialExists = false; + this.credentials.forEach(function(thisCredential) { + if (otherCredential.walletId === thisCredential.walletId) { + credentialExists = true; + } + }); + if (!credentialExists) { + newCredentials.push(otherCredential); + } + }); + + Array.prototype.push.apply(this.credentials, newCredentials); }; Profile.prototype.setChecked = function(ua, walletId) { From afe433d909e1da616803aacbaa34e15769981f12 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 15:31:51 +1200 Subject: [PATCH 4/6] Starting to migrate profile storage. --- src/js/services/storageService.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 75d34fe1a..1d7d03738 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -131,20 +131,31 @@ angular.module('copayApp.services') /** * - * @param {Profile} oldProfile + * @param {Profile} oldProfile * @param {Profile} secureProfile - may be falsy if no secure profile found. * @param {getProfileCallback} cb */ function _migrateProfiles(oldProfile, secureProfile, cb) { if (secureProfile) { + secureProfile.merge(oldProfile); } else { - root.storeNewProfile(oldProfile, function(err) { + root.storeNewProfile(secureProfile, function(err) { if (err) { cb(err, null); return; } + storage.remove('profile', function(err){ + if (err) { + cb(err, null); + return; + } + + cb(null, securePofile); + + }); + return; }); } @@ -161,18 +172,22 @@ angular.module('copayApp.services') var oldProfile; if (secureErr) { - return cb(secureErr); + return cb(secureErr, null); } if (secureStr) { try { secureProfile = Profile.fromString(secureStr); + $log.error('profile: ' + JSON.stringify(secureProfile)); } catch (e) { - var profileError = new Error('Could not read secure profile.'); - return cb(profileError, null); + $log.error(e); + return cb(e, null); } } + // Ignore insecure stuff for now + return cb(null, secureProfile); + storage.get('profile', function(getErr, str) { if (getErr) { return cb(getErr); From cc45e916805d92e7c877f1c6e2a679558c7837b0 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 20:23:01 +1200 Subject: [PATCH 5/6] Covered all the migration cases. --- src/js/services/storageService.js | 59 ++++++++++--------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 1d7d03738..be9637652 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -136,30 +136,29 @@ angular.module('copayApp.services') * @param {getProfileCallback} cb */ function _migrateProfiles(oldProfile, secureProfile, cb) { + var newProfile = oldProfile; + if (secureProfile) { secureProfile.merge(oldProfile); + newProfile = secureProfile; + } - } else { - root.storeNewProfile(secureProfile, function(err) { - if (err) { - cb(err, null); + root.storeNewProfile(newProfile, function(storeErr) { + if (storeErr) { + cb(storeErr, null); + return; + } + + storage.remove('profile', function(removeErr){ + if (removeErr) { + cb(removeErr, null); return; } - storage.remove('profile', function(err){ - if (err) { - cb(err, null); - return; - } - - cb(null, securePofile); - - }); - - return; + cb(null, newProfile); }); - } + }); }; /** @@ -178,25 +177,23 @@ angular.module('copayApp.services') if (secureStr) { try { secureProfile = Profile.fromString(secureStr); - $log.error('profile: ' + JSON.stringify(secureProfile)); + $log.debug('profile: ' + JSON.stringify(secureProfile)); } catch (e) { $log.error(e); return cb(e, null); } } - // Ignore insecure stuff for now - return cb(null, secureProfile); - - storage.get('profile', function(getErr, str) { + storage.get('profile', function(getErr, getStr) { if (getErr) { return cb(getErr); } - if (!str) { + if (!getStr) { if (secureProfile) { return cb(null, secureProfile); } else { + // No profiles found. No errors either. return cb(null, null); } } @@ -212,7 +209,6 @@ angular.module('copayApp.services') return(err, null); } - // Now we have to do a migration _migrateProfiles(oldProfile, secureProfile, cb); }); @@ -220,23 +216,6 @@ angular.module('copayApp.services') }); }; - /* - if (err || !str) - return cb(err); - - decryptOnMobile(str, function(err, str) { - if (err) return cb(err); - var p, err; - try { - p = Profile.fromString(str); - } catch (e) { - $log.debug('Could not read profile:', e); - err = new Error('Could not read profile:' + p); - } - return cb(err, p); - }); - */ - root.setFeedbackInfo = function(feedbackValues, cb) { storage.set('feedback', feedbackValues, cb); }; From 16b5054ea297acc00536c0cfe5ef5951df515b25 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 5 Jun 2018 11:34:14 +1200 Subject: [PATCH 6/6] Now including app version is profile. --- src/js/models/profile.js | 20 ++++++++++++++++---- src/js/services/profileService.js | 2 +- src/js/services/storageService.js | 7 +++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/js/models/profile.js b/src/js/models/profile.js index ac4b4828c..7690d1c2d 100644 --- a/src/js/models/profile.js +++ b/src/js/models/profile.js @@ -9,12 +9,12 @@ function Profile() { this.version = '1.0.0'; }; -Profile.create = function(opts) { - opts = opts || {}; +Profile.create = function(appVersion) { var x = new Profile(); + x.appVersion = appVersion; x.createdOn = Date.now(); - x.credentials = opts.credentials || []; + x.credentials = []; x.disclaimerAccepted = true; x.checked = {}; return x; @@ -23,6 +23,7 @@ Profile.create = function(opts) { Profile.fromObj = function(obj) { var x = new Profile(); + x.appVersion = obj.appVersion; x.createdOn = obj.createdOn; x.credentials = obj.credentials; x.disclaimerAccepted = obj.disclaimerAccepted; @@ -69,10 +70,12 @@ Profile.prototype.isDeviceChecked = function(ua) { Profile.prototype.merge = function(other) { var newCredentials = []; + var otherCredentialsLength = other.credentials.length; + var thisProfile = this; other.credentials.forEach(function(otherCredential) { var credentialExists = false; - this.credentials.forEach(function(thisCredential) { + thisProfile.credentials.forEach(function(thisCredential) { if (otherCredential.walletId === thisCredential.walletId) { credentialExists = true; } @@ -85,6 +88,15 @@ Profile.prototype.merge = function(other) { Array.prototype.push.apply(this.credentials, newCredentials); }; +/** + * It's a simple operation, but it means that all the profile logic stays + * in this file. + * @param {string} appVersion - ie "4.11.0" + */ +Profile.prototype.setAppVersion = function(appVersion) { + this.appVersion = appVersion; +} + Profile.prototype.setChecked = function(ua, walletId) { if (this.checkedUA != ua) { this.checkedUA = ua; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index dac88169f..25f2a6852 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -706,7 +706,7 @@ angular.module('copayApp.services') configService.get(function(err) { if (err) $log.debug(err); - var p = Profile.create(); + var p = Profile.create(appConfigService.version); storageService.storeNewProfile(p, function(err) { if (err) return cb(err); root.bindProfile(p, function(err) { diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index be9637652..2dc3d7511 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -1,6 +1,6 @@ 'use strict'; angular.module('copayApp.services') - .factory('storageService', function(logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, secureStorageService, $timeout) { + .factory('storageService', function(appConfigService, logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, secureStorageService, $timeout) { var root = {}; var storage; @@ -136,11 +136,14 @@ angular.module('copayApp.services') * @param {getProfileCallback} cb */ function _migrateProfiles(oldProfile, secureProfile, cb) { - var newProfile = oldProfile; + var newProfile; if (secureProfile) { secureProfile.merge(oldProfile); newProfile = secureProfile; + } else { + newProfile = oldProfile; + newProfile.setAppVersion(appConfigService.version); } root.storeNewProfile(newProfile, function(storeErr) {