From cc2f5f6a145531a0df08eaf2bacc3eb02d191d16 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 30 May 2018 16:28:32 +1200 Subject: [PATCH 01/15] Rudimentarily working with getting and setting the profile in the Keychain on iOS. --- app-template/config-template.xml | 1 + src/js/services/storageService.js | 55 ++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/app-template/config-template.xml b/app-template/config-template.xml index 8031c8110..32440782c 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -72,6 +72,7 @@ + diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 3d1ecfeef..ea0a975c0 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -116,19 +116,60 @@ angular.module('copayApp.services') }; root.storeNewProfile = function(profile, cb) { - storage.create('profile', profile.toObj(), cb); + console.log('storeNewProfile() 6'); + + SecureStorage.set('profile', profile.toObj(), function success(){ cb(); }, function error(err){ cb(err); }); + + //storage.create('profile', profile.toObj(), cb); }; root.storeProfile = function(profile, cb) { - storage.set('profile', profile.toObj(), cb); + console.log('storeProfile() 6'); + SecureStorage.set('profile', profile.toObj(), function success(){ cb(); }, function error(err){ cb(err); }); + //storage.set('profile', profile.toObj(), cb); }; root.getProfile = function(cb) { - storage.get('profile', function(err, str) { - if (err || !str) - return cb(err); + console.log("getProfile() 6"); + + SecureStorage.get( + 'profile', + function success(str) { + $log.debug('get profile returned success.'); + 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); + }); + }, + function error(err) { + $log.debug('get profile returned error.'); + $log.debug('returning error.'); + // Callback requires no error and no profile for creation of new profiles + return cb(); + } + ); + + + + /* + storage.get('profile', function(err, str) { + $log.debug('get profile returned.'); + if (err || !str) { + $log.debug('get profile returned error: ' + err + ' with string: ' + str); + return cb(err); + } + + $log.debug('calling decrypt'); decryptOnMobile(str, function(err, str) { + $log.debug('decrypt returned.'); if (err) return cb(err); var p, err; try { @@ -140,8 +181,12 @@ angular.module('copayApp.services') return cb(err, p); }); }); + */ + + }; + // Is this ever used? root.deleteProfile = function(cb) { storage.remove('profile', cb); }; From 90d321033bebcb0632c570abe581a57f41c1fa69 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Thu, 31 May 2018 17:53:58 +1200 Subject: [PATCH 02/15] Working pretty roughly on iOS with cordova-plugin-secure-storage. --- app-template/config-template.xml | 1 + src/js/services/secureStorageService.js | 93 +++++++++++++++++++++++++ src/js/services/storageService.js | 14 ++-- 3 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 src/js/services/secureStorageService.js diff --git a/app-template/config-template.xml b/app-template/config-template.xml index 8031c8110..e348cbe52 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -72,6 +72,7 @@ + diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js new file mode 100644 index 000000000..2c3a94703 --- /dev/null +++ b/src/js/services/secureStorageService.js @@ -0,0 +1,93 @@ +'use strict'; + +angular.module('copayApp.services').factory('secureStorageService', function($log, appConfigService, platformInfo) { + var root = {}; + + var ssIsReady = false; + var ssInitialisationFailed = false; + var pending = []; + + var ss = new cordova.plugins.SecureStorage( + function () { + console.log('ss Success'); + ssIsReady = true; + for (var i = 0; i < pending.length; i++) { + pending[i](); + } + pending = []; + }, + function (error) { + console.log('ss Error ' + error); + ssInitialisationFailed = true; + }, + appConfigService.packageNameId); + + + + root.get = function(key, cb) { + $log.debug('secureStorageService.get()'); + if (!ssIsReady) { + $log.debug("ss not ready."); + if (ssInitialisationFailed) { + $log.debug("returning error because initialisation failed."); + cb(new Error("Secure storage initialisation failed.")); + } else { + $log.debug("adding get to pending."); + pending.push(function(){ root.get(key, cb); }); + } + return + } + $log.debug("ss is ready."); + + ss.get( + function (value) { + console.log('ss Success, got ' + value); + cb(null, value); + }, + function (error) { + console.log('ss Error "' + error.message + '" ' + JSON.stringify(error)); + + if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain') { + $log.debug("Sending back null error."); + // The callback expects no error, but also no value, if it cannot be found. + cb(null); + } else { + cb(new Error(error)); + } + }, + key); + + }; + + root.set = function(key, value, cb) { + $log.debug('secureStorageService.set()'); + if (!ssIsReady) { + $log.debug("ss not ready."); + if (ssInitialisationFailed) { + $log.debug("returning error because initialisation failed."); + cb(new Error("Secure storage initialisation failed.")); + } else { + $log.debug("adding set to pending."); + pending.push(function(){ root.set(key, value, cb); }); + } + return + } + $log.debug("ss is ready."); + + ss.set( + function (value) { + console.log('ss Success, got ' + value); + cb(); + }, + function (error) { + console.log('ss Error ' + error); + cb(new Error(error)); + }, + key, value); + + }; + + + return root; +}); + diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 3d1ecfeef..05bf51a7a 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, $timeout) { + .factory('storageService', function(logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, secureStorageService, $timeout) { var root = {}; var storage; @@ -116,15 +116,21 @@ angular.module('copayApp.services') }; root.storeNewProfile = function(profile, cb) { - storage.create('profile', profile.toObj(), cb); + //storage.create('profile', profile.toObj(), cb); + secureStorageService.set('profile', profile.toObj(), cb); }; root.storeProfile = function(profile, cb) { - storage.set('profile', profile.toObj(), cb); + //storage.set('profile', profile.toObj(), cb); + secureStorageService.set('profile', profile.toObj(), cb); }; root.getProfile = function(cb) { - storage.get('profile', function(err, str) { + $log.debug("getProfile() 31 7"); + + //storage.get('profile', function(err, str) { + secureStorageService.get('profile', function(err, str) { + if (err || !str) return cb(err); From 4e6eb3295d682bbb86b4ec0d4e632b2e12a6bc0f Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Thu, 31 May 2018 18:08:04 +1200 Subject: [PATCH 03/15] Now works on Android too. --- src/js/services/secureStorageService.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js index 2c3a94703..7c374521b 100644 --- a/src/js/services/secureStorageService.js +++ b/src/js/services/secureStorageService.js @@ -47,7 +47,8 @@ angular.module('copayApp.services').factory('secureStorageService', function($lo function (error) { console.log('ss Error "' + error.message + '" ' + JSON.stringify(error)); - if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain') { + if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || + error.message === 'Key [_SS_profile] not found.') { $log.debug("Sending back null error."); // The callback expects no error, but also no value, if it cannot be found. cb(null); From fdc9a8c37b8c620c0181c4f851a87cb93cc1f10b Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 09:31:14 +1200 Subject: [PATCH 04/15] Tidy up. --- src/js/services/secureStorageService.js | 136 ++++++++++++------------ 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js index 7c374521b..989c1642a 100644 --- a/src/js/services/secureStorageService.js +++ b/src/js/services/secureStorageService.js @@ -3,89 +3,87 @@ angular.module('copayApp.services').factory('secureStorageService', function($log, appConfigService, platformInfo) { var root = {}; - var ssIsReady = false; - var ssInitialisationFailed = false; - var pending = []; + function CordovaSs() { + var isReady = false; + var initialisationFailed = false; + var pending = []; + + var storage = null; - var ss = new cordova.plugins.SecureStorage( - function () { - console.log('ss Success'); - ssIsReady = true; - for (var i = 0; i < pending.length; i++) { - pending[i](); - } - pending = []; - }, - function (error) { - console.log('ss Error ' + error); - ssInitialisationFailed = true; - }, - appConfigService.packageNameId); - - - root.get = function(key, cb) { - $log.debug('secureStorageService.get()'); - if (!ssIsReady) { - $log.debug("ss not ready."); - if (ssInitialisationFailed) { - $log.debug("returning error because initialisation failed."); + this.get = function(key, cb) { + if (!isReady) { + if (initialisationFailed) { cb(new Error("Secure storage initialisation failed.")); } else { - $log.debug("adding get to pending."); pending.push(function(){ root.get(key, cb); }); } return + } + + storage.get( + function (value) { + cb(null, value); + }, + function (error) { + if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || // iOS + error.message === 'Key [_SS_profile] not found.') { // Android + // The callback expects no error, but also no value, if it cannot be found. + cb(null); + } else { + cb(new Error(error)); + } + }, + key); } - $log.debug("ss is ready."); - ss.get( - function (value) { - console.log('ss Success, got ' + value); - cb(null, value); - }, - function (error) { - console.log('ss Error "' + error.message + '" ' + JSON.stringify(error)); - - if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || - error.message === 'Key [_SS_profile] not found.') { - $log.debug("Sending back null error."); - // The callback expects no error, but also no value, if it cannot be found. - cb(null); - } else { + this.set = function(key, value, cb) { + if (!isReady) { + if (initialisationFailed) { + cb(new Error("Secure storage initialisation failed.")); + } else { + pending.push(function(){ root.set(key, value, cb); }); + } + return + } + + storage.set( + function (value) { + cb(); + }, + function (error) { cb(new Error(error)); - } - }, - key); + }, + key, value); + } + if (platformInfo.isCordova) { + storage = new cordova.plugins.SecureStorage( + function () { + console.log('ss Success'); + isReady = true; + for (var i = 0; i < pending.length; i++) { + pending[i](); + } + spending = []; + }, + function (error) { + console.log('ss Error ' + error); + initialisationFailed = true; + }, + appConfigService.packageNameId); + } + + } + + var cordovaSs = new CordovaSs(); + + root.get = function(key, cb) { + cordovaSs.get(key, cb); }; root.set = function(key, value, cb) { - $log.debug('secureStorageService.set()'); - if (!ssIsReady) { - $log.debug("ss not ready."); - if (ssInitialisationFailed) { - $log.debug("returning error because initialisation failed."); - cb(new Error("Secure storage initialisation failed.")); - } else { - $log.debug("adding set to pending."); - pending.push(function(){ root.set(key, value, cb); }); - } - return - } - $log.debug("ss is ready."); - - ss.set( - function (value) { - console.log('ss Success, got ' + value); - cb(); - }, - function (error) { - console.log('ss Error ' + error); - cb(new Error(error)); - }, - key, value); - + cordovaSs.set(key, value, cb); }; From aaad6a1b4a7bfa5e940b23a0995a97a717f7f726 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 10:19:26 +1200 Subject: [PATCH 05/15] Refactored to have the mobile and desktop secure storage contained with a more generic secure storage service. --- app-template/config-template.xml | 3 +- src/js/services/mobileSecureStorageService.js | 93 +++++++++++++++ src/js/services/secureStorageService.js | 108 ++++-------------- src/js/services/storageService.js | 2 +- 4 files changed, 120 insertions(+), 86 deletions(-) create mode 100644 src/js/services/mobileSecureStorageService.js diff --git a/app-template/config-template.xml b/app-template/config-template.xml index e348cbe52..39b67d212 100644 --- a/app-template/config-template.xml +++ b/app-template/config-template.xml @@ -72,7 +72,8 @@ - + + diff --git a/src/js/services/mobileSecureStorageService.js b/src/js/services/mobileSecureStorageService.js new file mode 100644 index 000000000..93a2ec591 --- /dev/null +++ b/src/js/services/mobileSecureStorageService.js @@ -0,0 +1,93 @@ +'use strict'; + +angular.module('copayApp.services').factory('mobileSecureStorageService', function($log, appConfigService, platformInfo) { + var root = {}; + + var isReady = false; + var initialisationFailed = false; + var pending = []; + + var storage = null; + + this.get = function(key, cb) { + if (!isReady) { + if (initialisationFailed) { + cb(new Error("mobileSecureStorageService initialisation failed.")); + } else { + pending.push(function(){ root.get(key, cb); }); + } + return + } + + storage.get( + function (value) { + cb(null, value); + }, + function (error) { + if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || // iOS + error.message === 'Key [_SS_profile] not found.') { // Android + // The callback expects no error, but also no value, if it cannot be found. + cb(null, null); + } else { + cb(new Error(error)); + } + }, + key); + } + + this.set = function(key, value, cb) { + if (!isReady) { + if (initialisationFailed) { + cb(new Error("mobileSecureStorageService initialisation failed.")); + } else { + pending.push(function(){ root.set(key, value, cb); }); + } + return + } + + storage.set( + function (value) { + cb(); + }, + function (error) { + cb(new Error(error)); + }, + key, value); + } + + if (platformInfo.isCordova) { + storage = new cordova.plugins.SecureStorage( + function () { + $log.debug('mobileSecureStorageService initialised.'); + isReady = true; + for (var i = 0; i < pending.length; i++) { + pending[i](); + } + pending = []; + }, + function (error) { + c$log.debug('mobileSecureStorageService initialisation failed. ' + error); + initialisationFailed = true; + }, + appConfigService.packageNameId); + } + + root.get = function(key, cb) { + if (platformInfo.isMobile) { + storage.get(key, cb); + } else { + cb(new Error('mobileSecureStorageService is only available on mobile.')); + } + }; + + root.set = function(key, value, cb) { + if (platformInfo.isMobile) { + storage.set(key, v, cb); + } else { + cb(new Error('mobileSecureStorageService is only available on mobile.')); + } + }; + + return root; +}); + diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js index 989c1642a..c066109c2 100644 --- a/src/js/services/secureStorageService.js +++ b/src/js/services/secureStorageService.js @@ -1,92 +1,32 @@ 'use strict'; -angular.module('copayApp.services').factory('secureStorageService', function($log, appConfigService, platformInfo) { +angular.module('copayApp.services').factory('secureStorageService', function(desktopSecureStorageService, localStorageService, $log, mobileSecureStorageService, platformInfo) { var root = {}; - function CordovaSs() { - var isReady = false; - var initialisationFailed = false; - var pending = []; - - var storage = null; - - - this.get = function(key, cb) { - if (!isReady) { - if (initialisationFailed) { - cb(new Error("Secure storage initialisation failed.")); - } else { - pending.push(function(){ root.get(key, cb); }); - } - return - } - - storage.get( - function (value) { - cb(null, value); - }, - function (error) { - if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || // iOS - error.message === 'Key [_SS_profile] not found.') { // Android - // The callback expects no error, but also no value, if it cannot be found. - cb(null); - } else { - cb(new Error(error)); - } - }, - key); - } - - this.set = function(key, value, cb) { - if (!isReady) { - if (initialisationFailed) { - cb(new Error("Secure storage initialisation failed.")); - } else { - pending.push(function(){ root.set(key, value, cb); }); - } - return - } - - storage.set( - function (value) { - cb(); - }, - function (error) { - cb(new Error(error)); - }, - key, value); - } - - if (platformInfo.isCordova) { - storage = new cordova.plugins.SecureStorage( - function () { - console.log('ss Success'); - isReady = true; - for (var i = 0; i < pending.length; i++) { - pending[i](); - } - spending = []; - }, - function (error) { - console.log('ss Error ' + error); - initialisationFailed = true; - }, - appConfigService.packageNameId); - } - + // To make wrong code look wrong + function alteredKeyIndicatingDesireForSecureStorage(key) { + return key + ":desiredSecure"; } - var cordovaSs = new CordovaSs(); - - root.get = function(key, cb) { - cordovaSs.get(key, cb); - }; - - root.set = function(key, value, cb) { - cordovaSs.set(key, value, cb); - }; - + root.get = function(k, cb) { + if (platformInfo.isMobile) { + mobileSecureStorageService.get(k, cb); + } else if (platformInfo.isNW) { + desktopSecureStorageService.get(k, cb); + } else { // Browser + localStorageService.get(alteredKeyIndicatingDesireForSecureStorage(k), cb); + } + } + root.set = function(k, v, cb) { + if (platformInfo.isMobile) { + mobileSecureStorageService.set(k, v, cb); + } else if (platformInfo.isNW) { + desktopSecureStorageService.set(k, v, cb); + } else { // Browser + localStorageService.set(alteredKeyIndicatingDesireForSecureStorage(k), v, cb); + } + } + return root; -}); - +}); \ No newline at end of file diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 05bf51a7a..2cc6da730 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -126,7 +126,7 @@ angular.module('copayApp.services') }; root.getProfile = function(cb) { - $log.debug("getProfile() 31 7"); + $log.debug("getProfile() 1 8"); //storage.get('profile', function(err, str) { secureStorageService.get('profile', function(err, str) { From 17685dd810d46895e04fa7489eb9fcca66f1e1a2 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 10:42:38 +1200 Subject: [PATCH 06/15] Fixed some issues after the refactor. --- src/js/services/mobileSecureStorageService.js | 104 +++++++++--------- src/js/services/secureStorageService.js | 3 + 2 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/js/services/mobileSecureStorageService.js b/src/js/services/mobileSecureStorageService.js index 93a2ec591..56c3e2df6 100644 --- a/src/js/services/mobileSecureStorageService.js +++ b/src/js/services/mobileSecureStorageService.js @@ -9,52 +9,6 @@ angular.module('copayApp.services').factory('mobileSecureStorageService', functi var storage = null; - this.get = function(key, cb) { - if (!isReady) { - if (initialisationFailed) { - cb(new Error("mobileSecureStorageService initialisation failed.")); - } else { - pending.push(function(){ root.get(key, cb); }); - } - return - } - - storage.get( - function (value) { - cb(null, value); - }, - function (error) { - if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || // iOS - error.message === 'Key [_SS_profile] not found.') { // Android - // The callback expects no error, but also no value, if it cannot be found. - cb(null, null); - } else { - cb(new Error(error)); - } - }, - key); - } - - this.set = function(key, value, cb) { - if (!isReady) { - if (initialisationFailed) { - cb(new Error("mobileSecureStorageService initialisation failed.")); - } else { - pending.push(function(){ root.set(key, value, cb); }); - } - return - } - - storage.set( - function (value) { - cb(); - }, - function (error) { - cb(new Error(error)); - }, - key, value); - } - if (platformInfo.isCordova) { storage = new cordova.plugins.SecureStorage( function () { @@ -73,19 +27,65 @@ angular.module('copayApp.services').factory('mobileSecureStorageService', functi } root.get = function(key, cb) { - if (platformInfo.isMobile) { - storage.get(key, cb); - } else { + + if (!platformInfo.isMobile) { cb(new Error('mobileSecureStorageService is only available on mobile.')); + return; } + + if (!isReady) { + if (initialisationFailed) { + cb(new Error('mobileSecureStorageService initialisation failed.')); + } else { + $log.debug('mss.get() queued.'); + pending.push(function(){ root.get(key, cb); }); + } + return; + } + + $log.debug('mss.get() running.'); + storage.get( + function (value) { + $log.debug('mss.get() succeeded.'); + cb(null, value); + }, + function (error) { + $log.debug('mss get failed. ' + error); + if (error.message === 'Failure in SecureStorage.get() - The specified item could not be found in the keychain' || // iOS + error.message === 'Key [_SS_profile] not found.') { // Android + // The callback expects no error, but also no value, if it cannot be found. + cb(null, null); + } else { + cb(new Error(error)); + } + }, + key); }; root.set = function(key, value, cb) { - if (platformInfo.isMobile) { - storage.set(key, v, cb); - } else { + + if (!platformInfo.isMobile) { cb(new Error('mobileSecureStorageService is only available on mobile.')); + } + + if (!isReady) { + if (initialisationFailed) { + cb(new Error('mobileSecureStorageService initialisation failed.')); + } else { + pending.push(function(){ root.set(key, value, cb); }); + } + return; } + + storage.set( + function (value) { + cb(); + }, + function (error) { + cb(new Error(error)); + }, + key, value); + }; return root; diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js index c066109c2..e7179bf62 100644 --- a/src/js/services/secureStorageService.js +++ b/src/js/services/secureStorageService.js @@ -9,9 +9,12 @@ angular.module('copayApp.services').factory('secureStorageService', function(des } root.get = function(k, cb) { + $log.debug('ss.get()'); if (platformInfo.isMobile) { + $log.debug('ss.get() using mobile.'); mobileSecureStorageService.get(k, cb); } else if (platformInfo.isNW) { + $log.debug('ss.get() using desktop.'); desktopSecureStorageService.get(k, cb); } else { // Browser localStorageService.get(alteredKeyIndicatingDesireForSecureStorage(k), cb); From f483fd81d8902bcd01c05f740dc8ebfd309dbb77 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 11:53:04 +1200 Subject: [PATCH 07/15] Removed deleteProfile() as it is not used. --- src/js/services/storageService.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index 2cc6da730..df135a946 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -116,19 +116,16 @@ angular.module('copayApp.services') }; root.storeNewProfile = function(profile, cb) { - //storage.create('profile', profile.toObj(), cb); secureStorageService.set('profile', profile.toObj(), cb); }; root.storeProfile = function(profile, cb) { - //storage.set('profile', profile.toObj(), cb); secureStorageService.set('profile', profile.toObj(), cb); }; root.getProfile = function(cb) { $log.debug("getProfile() 1 8"); - //storage.get('profile', function(err, str) { secureStorageService.get('profile', function(err, str) { if (err || !str) @@ -148,10 +145,6 @@ angular.module('copayApp.services') }); }; - root.deleteProfile = function(cb) { - storage.remove('profile', cb); - }; - root.setFeedbackInfo = function(feedbackValues, cb) { storage.set('feedback', feedbackValues, cb); }; From c5121afd7ccd99db0ba02f0ad7c7d755f88c799c Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 15:04:52 +1200 Subject: [PATCH 08/15] Placeholder desktopSecureStorageService. --- src/js/services/desktopSecureStorageService.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/js/services/desktopSecureStorageService.js diff --git a/src/js/services/desktopSecureStorageService.js b/src/js/services/desktopSecureStorageService.js new file mode 100644 index 000000000..6e148da2c --- /dev/null +++ b/src/js/services/desktopSecureStorageService.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('copayApp.services').factory('desktopSecureStorageService', function($log) { + // Placeholder + return {}; +}); \ No newline at end of file From 8d94a244bcfd71934b85305f750a0735c97977ca Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Fri, 1 Jun 2018 15:25:08 +1200 Subject: [PATCH 09/15] Removed some debug messages. --- src/js/services/mobileSecureStorageService.js | 5 ----- src/js/services/secureStorageService.js | 3 --- src/js/services/storageService.js | 2 -- 3 files changed, 10 deletions(-) diff --git a/src/js/services/mobileSecureStorageService.js b/src/js/services/mobileSecureStorageService.js index 56c3e2df6..f9994fdf8 100644 --- a/src/js/services/mobileSecureStorageService.js +++ b/src/js/services/mobileSecureStorageService.js @@ -12,7 +12,6 @@ angular.module('copayApp.services').factory('mobileSecureStorageService', functi if (platformInfo.isCordova) { storage = new cordova.plugins.SecureStorage( function () { - $log.debug('mobileSecureStorageService initialised.'); isReady = true; for (var i = 0; i < pending.length; i++) { pending[i](); @@ -20,7 +19,6 @@ angular.module('copayApp.services').factory('mobileSecureStorageService', functi pending = []; }, function (error) { - c$log.debug('mobileSecureStorageService initialisation failed. ' + error); initialisationFailed = true; }, appConfigService.packageNameId); @@ -37,16 +35,13 @@ angular.module('copayApp.services').factory('mobileSecureStorageService', functi if (initialisationFailed) { cb(new Error('mobileSecureStorageService initialisation failed.')); } else { - $log.debug('mss.get() queued.'); pending.push(function(){ root.get(key, cb); }); } return; } - $log.debug('mss.get() running.'); storage.get( function (value) { - $log.debug('mss.get() succeeded.'); cb(null, value); }, function (error) { diff --git a/src/js/services/secureStorageService.js b/src/js/services/secureStorageService.js index e7179bf62..c066109c2 100644 --- a/src/js/services/secureStorageService.js +++ b/src/js/services/secureStorageService.js @@ -9,12 +9,9 @@ angular.module('copayApp.services').factory('secureStorageService', function(des } root.get = function(k, cb) { - $log.debug('ss.get()'); if (platformInfo.isMobile) { - $log.debug('ss.get() using mobile.'); mobileSecureStorageService.get(k, cb); } else if (platformInfo.isNW) { - $log.debug('ss.get() using desktop.'); desktopSecureStorageService.get(k, cb); } else { // Browser localStorageService.get(alteredKeyIndicatingDesireForSecureStorage(k), cb); diff --git a/src/js/services/storageService.js b/src/js/services/storageService.js index df135a946..7c4ad0a60 100644 --- a/src/js/services/storageService.js +++ b/src/js/services/storageService.js @@ -124,8 +124,6 @@ angular.module('copayApp.services') }; root.getProfile = function(cb) { - $log.debug("getProfile() 1 8"); - secureStorageService.get('profile', function(err, str) { if (err || !str) From 4109d4743f184b6e865615f735a4f3fdaff5ff26 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Mon, 4 Jun 2018 09:47:02 +1200 Subject: [PATCH 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 15/15] 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) {