From f43186d3e1d14a56a3a6c847561fa582444e447a Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 21 May 2018 21:26:36 -0500 Subject: [PATCH] Async SF API --- .babelrc | 8 ++- .../app/directives/views/accountMenu.js | 38 ++++++----- .../app/directives/views/passwordWizard.js | 7 +- app/assets/javascripts/app/models/api/item.js | 2 +- .../app/models/local/itemParams.js | 19 +++--- .../app/services/actionsManager.js | 56 +++++++--------- .../app/services/archiveManager.js | 19 +++--- .../javascripts/app/services/authManager.js | 67 ++++++------------- .../app/services/componentManager.js | 2 +- .../app/services/desktopManager.js | 20 +++--- .../javascripts/app/services/modelManager.js | 27 ++++---- .../app/services/passcodeManager.js | 20 +++--- .../app/services/storageManager.js | 8 ++- .../javascripts/app/services/syncManager.js | 53 ++++++++------- package-lock.json | 9 +++ package.json | 1 + 16 files changed, 180 insertions(+), 176 deletions(-) diff --git a/.babelrc b/.babelrc index 002b4aa0d..ddfd24646 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,9 @@ { - "presets": ["env"] + "presets": ["env"], + "plugins": [ + ["transform-runtime", { + "polyfill": false, + "regenerator": true + }] + ] } diff --git a/app/assets/javascripts/app/directives/views/accountMenu.js b/app/assets/javascripts/app/directives/views/accountMenu.js index 22171a601..577213257 100644 --- a/app/assets/javascripts/app/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/directives/views/accountMenu.js @@ -13,7 +13,7 @@ class AccountMenu { $timeout, storageManager, $compile, archiveManager) { 'ngInject'; - $scope.formData = {mergeLocal: true, url: syncManager.serverURL, ephemeral: false}; + $scope.formData = {mergeLocal: true, url: syncManager.serverURL, ephemeral: false, email: "a@bitar.io", user_password: "password"}; $scope.user = authManager.user; $scope.server = syncManager.serverURL; @@ -262,26 +262,28 @@ class AccountMenu { }.bind(this) if(data.auth_params) { - SFJS.crypto.computeEncryptionKeysForUser(password, data.auth_params, (keys) => { + SFJS.crypto.computeEncryptionKeysForUser(password, data.auth_params).then((keys) => { try { - SFItemTransformer.decryptMultipleItems(data.items, keys, false); /* throws = false as we don't want to interrupt all decryption if just one fails */ - // delete items enc_item_key since the user's actually key will do the encrypting once its passed off - data.items.forEach(function(item){ - item.enc_item_key = null; - item.auth_hash = null; - }); + SFJS.itemTransformer.decryptMultipleItems(data.items, keys, false) /* throws = false as we don't want to interrupt all decryption if just one fails */ + .then(() => { + // delete items enc_item_key since the user's actually key will do the encrypting once its passed off + data.items.forEach(function(item){ + item.enc_item_key = null; + item.auth_hash = null; + }); - var errorCount = 0; - // Don't import items that didn't decrypt properly - data.items = data.items.filter(function(item){ - if(item.errorDecrypting) { - errorCount++; - return false; - } - return true; + var errorCount = 0; + // Don't import items that didn't decrypt properly + data.items = data.items.filter(function(item){ + if(item.errorDecrypting) { + errorCount++; + return false; + } + return true; + }) + + onDataReady(errorCount); }) - - onDataReady(errorCount); } catch (e) { console.log("Error decrypting", e); diff --git a/app/assets/javascripts/app/directives/views/passwordWizard.js b/app/assets/javascripts/app/directives/views/passwordWizard.js index b184cb9c2..9deb37427 100644 --- a/app/assets/javascripts/app/directives/views/passwordWizard.js +++ b/app/assets/javascripts/app/directives/views/passwordWizard.js @@ -174,7 +174,7 @@ class PasswordWizard { // Ensure value for current password matches what's saved let authParams = authManager.getAuthParams(); let password = $scope.formData.currentPassword; - SFJS.crypto.computeEncryptionKeysForUser(password, authParams, (keys) => { + SFJS.crypto.computeEncryptionKeysForUser(password, authParams).then((keys) => { let success = keys.mk === authManager.keys().mk; if(success) { this.currentServerPw = keys.pw; @@ -202,7 +202,10 @@ class PasswordWizard { let currentServerPw = this.currentServerPw; - SFJS.crypto.generateInitialEncryptionKeysForUser(authManager.user.email, newUserPassword, (newKeys, newAuthParams) => { + SFJS.crypto.generateInitialEncryptionKeysForUser(authManager.user.email, newUserPassword).then((results) => { + let newKeys = results.newKeys; + let newAuthParams = results.newAuthParams; + // perform a sync beforehand to pull in any last minutes changes before we change the encryption key (and thus cant decrypt new changes) syncManager.sync((response) => { authManager.changePassword(currentServerPw, newKeys, newAuthParams, (response) => { diff --git a/app/assets/javascripts/app/models/api/item.js b/app/assets/javascripts/app/models/api/item.js index fe85f5504..b3ebec65d 100644 --- a/app/assets/javascripts/app/models/api/item.js +++ b/app/assets/javascripts/app/models/api/item.js @@ -9,7 +9,7 @@ class Item { this.observers = []; if(!this.uuid) { - this.uuid = SFJS.crypto.generateUUID(); + this.uuid = SFJS.crypto.generateUUIDSync(); } } diff --git a/app/assets/javascripts/app/models/local/itemParams.js b/app/assets/javascripts/app/models/local/itemParams.js index 04eab536d..c59de929a 100644 --- a/app/assets/javascripts/app/models/local/itemParams.js +++ b/app/assets/javascripts/app/models/local/itemParams.js @@ -3,34 +3,35 @@ class ItemParams { constructor(item, keys, version) { this.item = item; this.keys = keys; - this.version = version || SFJS.crypto.version(); + this.version = version || SFJS.version(); } - paramsForExportFile(includeDeleted) { + async paramsForExportFile(includeDeleted) { this.additionalFields = ["updated_at"]; this.forExportFile = true; if(includeDeleted) { return this.__params(); } else { - return _.omit(this.__params(), ["deleted"]); + var result = await this.__params(); + return _.omit(result, ["deleted"]); } } - paramsForExtension() { + async paramsForExtension() { return this.paramsForExportFile(); } - paramsForLocalStorage() { + async paramsForLocalStorage() { this.additionalFields = ["updated_at", "dirty", "errorDecrypting"]; this.forExportFile = true; return this.__params(); } - paramsForSync() { + async paramsForSync() { return this.__params(); } - __params() { + async __params() { console.assert(!this.item.dummy, "Item is dummy, should not have gotten here.", this.item.dummy) @@ -39,7 +40,7 @@ class ItemParams { // Items should always be encrypted for export files. Only respect item.doNotEncrypt for remote sync params. var doNotEncrypt = this.item.doNotEncrypt() && !this.forExportFile; if(this.keys && !doNotEncrypt) { - var encryptedParams = SFItemTransformer.encryptItem(this.item, this.keys, this.version); + var encryptedParams = await SFJS.itemTransformer.encryptItem(this.item, this.keys, this.version); _.merge(params, encryptedParams); if(this.version !== "001") { @@ -47,7 +48,7 @@ class ItemParams { } } else { - params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + SFJS.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties())); + params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + await SFJS.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties())); if(!this.forExportFile) { params.enc_item_key = null; params.auth_hash = null; diff --git a/app/assets/javascripts/app/services/actionsManager.js b/app/assets/javascripts/app/services/actionsManager.js index f14c7e33a..176d4883d 100644 --- a/app/assets/javascripts/app/services/actionsManager.js +++ b/app/assets/javascripts/app/services/actionsManager.js @@ -60,17 +60,18 @@ class ActionsManager { switch (action.verb) { case "get": { - this.httpManager.getAbsolute(action.url, {}, function(response){ + this.httpManager.getAbsolute(action.url, {}, (response) => { action.error = false; var items = response.items || [response.item]; - SFItemTransformer.decryptMultipleItems(items, this.authManager.keys()); - items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved); - for(var item of items) { - item.setDirty(true); - } - this.syncManager.sync(null); - customCallback({items: items}); - }.bind(this), function(response){ + SFJS.itemTransformer.decryptMultipleItems(items, this.authManager.keys()).then(() => { + items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved); + for(var item of items) { + item.setDirty(true); + } + this.syncManager.sync(null); + customCallback({items: items}); + }) + }, (response) => { action.error = true; customCallback(null); }) @@ -80,13 +81,13 @@ class ActionsManager { case "render": { - this.httpManager.getAbsolute(action.url, {}, function(response){ + this.httpManager.getAbsolute(action.url, {}, (response) => { action.error = false; - SFItemTransformer.decryptItem(response.item, this.authManager.keys()); - var item = this.modelManager.createItem(response.item, true /* Dont notify observers */); - customCallback({item: item}); - - }.bind(this), function(response){ + SFJS.itemTransformer.decryptItem(response.item, this.authManager.keys()).then(() => { + var item = this.modelManager.createItem(response.item, true /* Dont notify observers */); + customCallback({item: item}); + }) + }, (response) => { action.error = true; customCallback(null); }) @@ -102,22 +103,15 @@ class ActionsManager { } case "post": { - var params = {}; + this.outgoingParamsForItem(item, extension, decrypted).then((itemParams) => { + var params = { + items: [itemParams] // Wrap it in an array + } - if(action.all) { - var items = this.modelManager.allItemsMatchingTypes(action.content_types); - params.items = items.map(function(item){ - var params = this.outgoingParamsForItem(item, extension, decrypted); - return params; - }.bind(this)) - - } else { - params.items = [this.outgoingParamsForItem(item, extension, decrypted)]; - } - - this.performPost(action, extension, params, function(response){ - customCallback(response); - }); + this.performPost(action, extension, params, function(response){ + customCallback(response); + }); + }) break; } @@ -130,7 +124,7 @@ class ActionsManager { action.lastExecuted = new Date(); } - outgoingParamsForItem(item, extension, decrypted = false) { + async outgoingParamsForItem(item, extension, decrypted = false) { var keys = this.authManager.keys(); if(decrypted) { keys = null; diff --git a/app/assets/javascripts/app/services/archiveManager.js b/app/assets/javascripts/app/services/archiveManager.js index cc0bf7dc7..86890859a 100644 --- a/app/assets/javascripts/app/services/archiveManager.js +++ b/app/assets/javascripts/app/services/archiveManager.js @@ -24,22 +24,23 @@ class ArchiveManager { protocolVersion = this.authManager.protocolVersion(); } } - var data = this.__itemsData(keys, authParams, protocolVersion); - this.__downloadData(data, `SN Archive - ${new Date()}.txt`); + this.__itemsData(keys, authParams, protocolVersion).then((data) => { + this.__downloadData(data, `SN Archive - ${new Date()}.txt`); - // download as zipped plain text files - if(!keys) { - var notes = this.modelManager.allItemsMatchingTypes(["Note"]); - this.__downloadZippedNotes(notes); - } + // download as zipped plain text files + if(!keys) { + var notes = this.modelManager.allItemsMatchingTypes(["Note"]); + this.__downloadZippedNotes(notes); + } + }) } /* Private */ - __itemsData(keys, authParams, protocolVersion) { - let data = this.modelManager.getAllItemsJSONData(keys, authParams, protocolVersion); + async __itemsData(keys, authParams, protocolVersion) { + let data = await this.modelManager.getAllItemsJSONData(keys, authParams, protocolVersion); let blobData = new Blob([data], {type: 'text/json'}); return blobData; } diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js index f1ed01afb..5712044c7 100644 --- a/app/assets/javascripts/app/services/authManager.js +++ b/app/assets/javascripts/app/services/authManager.js @@ -83,37 +83,8 @@ angular.module('app') } } - this.costMinimumForVersion = function(version) { - // all current versions have a min of 3000 - // future versions will increase this - return SFJS.crypto.costMinimumForVersion(version); - } - this.isProtocolVersionSupported = function(version) { - return SFJS.crypto.supportedVersions().includes(version); - } - - /* Upon sign in to an outdated version, the user will be presented with an alert requiring them to confirm - understanding they are signing in with an older version of the protocol, and must upgrade immediately after completing sign in. - */ - this.isProtocolVersionOutdated = function(version) { - // YYYY-MM-DD - let expirationDates = { - "001" : Date.parse("2018-01-01"), - "002" : Date.parse("2019-06-01"), - } - - let date = expirationDates[version]; - if(!date) { - // No expiration date, is active version - return false; - } - let expired = new Date() > date; - return expired; - } - - this.supportsPasswordDerivationCost = function(cost) { - return SFJS.crypto.supportsPasswordDerivationCost(cost); + return SFJS.supportedVersions().includes(version); } this.getAuthParamsForEmail = function(url, email, extraParams, callback) { @@ -130,7 +101,7 @@ angular.module('app') } this.login = function(url, email, password, ephemeral, strictSignin, extraParams, callback) { - this.getAuthParamsForEmail(url, email, extraParams, function(authParams){ + this.getAuthParamsForEmail(url, email, extraParams, (authParams) => { // SF3 requires a unique identifier in the auth params authParams.identifier = email; @@ -147,7 +118,7 @@ angular.module('app') if(!this.isProtocolVersionSupported(authParams.version)) { var message; - if(SFJS.crypto.isVersionNewerThanLibraryVersion(authParams.version)) { + if(SFJS.isVersionNewerThanLibraryVersion(authParams.version)) { // The user has a new account type, but is signing in to an older client. message = "This version of the application does not support your newer account type. Please upgrade to the latest version of Standard Notes to sign in."; } else { @@ -158,14 +129,14 @@ angular.module('app') return; } - if(this.isProtocolVersionOutdated(authParams.version)) { + if(SFJS.isProtocolVersionOutdated(authParams.version)) { let message = `The encryption version for your account, ${authParams.version}, is outdated and requires upgrade. You may proceed with login, but are advised to follow prompts for Security Updates once inside. Please visit standardnotes.org/help/security for more information.\n\nClick 'OK' to proceed with login.` if(!confirm(message)) { return; } } - if(!this.supportsPasswordDerivationCost(authParams.pw_cost)) { + if(!SFJS.supportsPasswordDerivationCost(authParams.pw_cost)) { let message = "Your account was created on a platform with higher security capabilities than this browser supports. " + "If we attempted to generate your login keys here, it would take hours. " + "Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to log in." @@ -173,7 +144,7 @@ angular.module('app') return; } - var minimum = this.costMinimumForVersion(authParams.version); + var minimum = SFJS.costMinimumForVersion(authParams.version); if(authParams.pw_cost < minimum) { let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/security for more information."; callback({error: {message: message}}); @@ -182,7 +153,7 @@ angular.module('app') if(strictSignin) { // Refuse sign in if authParams.version is anything but the latest version - var latestVersion = SFJS.crypto.version(); + var latestVersion = SFJS.version(); if(authParams.version !== latestVersion) { let message = `Strict sign in refused server sign in parameters. The latest security version is ${latestVersion}, but your account is reported to have version ${authParams.version}. If you'd like to proceed with sign in anyway, please disable strict sign in and try again.`; callback({error: {message: message}}); @@ -190,24 +161,25 @@ angular.module('app') } } - SFJS.crypto.computeEncryptionKeysForUser(password, authParams, function(keys){ + SFJS.crypto.computeEncryptionKeysForUser(password, authParams).then((keys) => { var requestUrl = url + "/auth/sign_in"; var params = _.merge({password: keys.pw, email: email}, extraParams); - httpManager.postAbsolute(requestUrl, params, function(response){ + + httpManager.postAbsolute(requestUrl, params, (response) => { this.setEphemeral(ephemeral); this.handleAuthResponse(response, email, url, authParams, keys); this.checkForSecurityUpdate(); $timeout(() => callback(response)); - }.bind(this), function(response){ + }, (response) => { console.error("Error logging in", response); if(typeof response !== 'object') { response = {error: {message: "A server error occurred while trying to sign in. Please try again."}}; } $timeout(() => callback(response)); - }) + }); - }.bind(this)); - }.bind(this)) + }); + }) } this.handleAuthResponse = function(response, email, url, authParams, keys) { @@ -238,7 +210,10 @@ angular.module('app') } this.register = function(url, email, password, ephemeral, callback) { - SFJS.crypto.generateInitialEncryptionKeysForUser(email, password, (keys, authParams) => { + SFJS.crypto.generateInitialEncryptionKeysForUser(email, password).then((results) => { + let keys = results.keys; + let authParams = results.authParams; + var requestUrl = url + "/auth"; var params = _.merge({password: keys.pw, email: email}, authParams); @@ -277,12 +252,12 @@ angular.module('app') this.updateAuthParams = function(authParams, callback) { var requestUrl = storageManager.getItem("server") + "/auth/update"; var params = authParams; - httpManager.postAbsolute(requestUrl, params, function(response) { + httpManager.postAbsolute(requestUrl, params, (response) => { storageManager.setItem("auth_params", JSON.stringify(authParams)); if(callback) { callback(response); } - }.bind(this), function(response){ + }, function(response){ var error = response; console.error("Update error:", response); if(callback) { @@ -297,7 +272,7 @@ angular.module('app') return; } - let latest = SFJS.crypto.version(); + let latest = SFJS.version(); if(this.protocolVersion() !== latest) { // Prompt user to perform security update diff --git a/app/assets/javascripts/app/services/componentManager.js b/app/assets/javascripts/app/services/componentManager.js index 4ebf7809e..8808126a5 100644 --- a/app/assets/javascripts/app/services/componentManager.js +++ b/app/assets/javascripts/app/services/componentManager.js @@ -716,7 +716,7 @@ class ComponentManager { console.log("Web|componentManager|registerComponentWindow", component); } component.window = componentWindow; - component.sessionKey = SFJS.crypto.generateUUID(); + component.sessionKey = SFJS.crypto.generateUUIDSync(); this.sendMessageToComponent(component, { action: "component-registered", sessionKey: component.sessionKey, diff --git a/app/assets/javascripts/app/services/desktopManager.js b/app/assets/javascripts/app/services/desktopManager.js index 29c370150..23edc562d 100644 --- a/app/assets/javascripts/app/services/desktopManager.js +++ b/app/assets/javascripts/app/services/desktopManager.js @@ -36,7 +36,7 @@ class DesktopManager { Sending a component in its raw state is really slow for the desktop app Keys are not passed into ItemParams, so the result is not encrypted */ - convertComponentForTransmission(component) { + async convertComponentForTransmission(component) { return new ItemParams(component).paramsForExportFile(true); } @@ -44,14 +44,15 @@ class DesktopManager { syncComponentsInstallation(components) { if(!this.isDesktop) return; - var data = components.map((component) => { + Promise.all(components.map((component) => { return this.convertComponentForTransmission(component); + })).then((data) => { + this.installationSyncHandler(data); }) - this.installationSyncHandler(data); } - installComponent(component) { - this.installComponentHandler(this.convertComponentForTransmission(component)); + async installComponent(component) { + this.installComponentHandler(await this.convertComponentForTransmission(component)); } registerUpdateObserver(callback) { @@ -128,7 +129,7 @@ class DesktopManager { } } - desktop_requestBackupFile() { + desktop_requestBackupFile(callback) { var keys, authParams, protocolVersion; if(this.authManager.offline() && this.passcodeManager.hasPasscode()) { keys = this.passcodeManager.keys(); @@ -140,13 +141,14 @@ class DesktopManager { protocolVersion = this.authManager.protocolVersion(); } - let data = this.modelManager.getAllItemsJSONData( + this.modelManager.getAllItemsJSONData( keys, authParams, protocolVersion, true /* return null on empty */ - ); - return data; + ).then((data) => { + callback(data); + }) } desktop_setMajorDataChangeHandler(handler) { diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 3ba84cf01..f41626325 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -59,7 +59,7 @@ class ModelManager { var newItem = this.createItem(item); - newItem.uuid = SFJS.crypto.generateUUID(); + newItem.uuid = SFJS.crypto.generateUUIDSync(); // Update uuids of relationships newItem.informReferencesOfUUIDChange(item.uuid, newItem.uuid); @@ -455,24 +455,25 @@ class ModelManager { Archives */ - getAllItemsJSONData(keys, authParams, protocolVersion, returnNullIfEmpty) { - var items = _.map(this.allItems, (item) => { + async getAllItemsJSONData(keys, authParams, protocolVersion, returnNullIfEmpty) { + return Promise.all(this.allItems.map((item) => { var itemParams = new ItemParams(item, keys, protocolVersion); return itemParams.paramsForExportFile(); - }); + })).then((items) => { + if(returnNullIfEmpty && items.length == 0) { + return null; + } - if(returnNullIfEmpty && items.length == 0) { - return null; - } + var data = {items: items} - var data = {items: items} + if(keys) { + // auth params are only needed when encrypted with a standard file key + data["auth_params"] = authParams; + } - if(keys) { - // auth params are only needed when encrypted with a standard file key - data["auth_params"] = authParams; - } + return JSON.stringify(data, null, 2 /* pretty print */); + }) - return JSON.stringify(data, null, 2 /* pretty print */); } diff --git a/app/assets/javascripts/app/services/passcodeManager.js b/app/assets/javascripts/app/services/passcodeManager.js index e359cebc4..1315350e1 100644 --- a/app/assets/javascripts/app/services/passcodeManager.js +++ b/app/assets/javascripts/app/services/passcodeManager.js @@ -32,7 +32,7 @@ angular.module('app') this.unlock = function(passcode, callback) { var params = this.passcodeAuthParams(); - SFJS.crypto.computeEncryptionKeysForUser(passcode, params, (keys) => { + SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => { if(keys.pw !== params.hash) { callback(false); return; @@ -40,16 +40,20 @@ angular.module('app') this._keys = keys; this._authParams = params; - this.decryptLocalStorage(keys, params); - this._locked = false; - callback(true); + this.decryptLocalStorage(keys, params).then(() => { + this._locked = false; + callback(true); + }) }); } this.setPasscode = (passcode, callback) => { - var uuid = SFJS.crypto.generateUUID(); + var uuid = SFJS.crypto.generateUUIDSync(); + + SFJS.crypto.generateInitialEncryptionKeysForUser(uuid, passcode).then((results) => { + let keys = results.keys; + let authParams = results.authParams; - SFJS.crypto.generateInitialEncryptionKeysForUser(uuid, passcode, (keys, authParams) => { authParams.hash = keys.pw; this._keys = keys; this._hasPasscode = true; @@ -83,9 +87,9 @@ angular.module('app') storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true); } - this.decryptLocalStorage = function(keys, authParams) { + this.decryptLocalStorage = async function(keys, authParams) { storageManager.setKeys(keys, authParams); - storageManager.decryptStorage(); + return storageManager.decryptStorage(); } } }); diff --git a/app/assets/javascripts/app/services/storageManager.js b/app/assets/javascripts/app/services/storageManager.js index 957fbd694..6c52e8830 100644 --- a/app/assets/javascripts/app/services/storageManager.js +++ b/app/assets/javascripts/app/services/storageManager.js @@ -154,12 +154,14 @@ class StorageManager { // Save new encrypted storage in Fixed storage var params = new ItemParams(encryptedStorage, this.encryptedStorageKeys, this.encryptedStorageAuthParams.version); - this.setItem("encryptedStorage", JSON.stringify(params.paramsForSync()), StorageManager.Fixed); + params.paramsForSync().then((syncParams) => { + this.setItem("encryptedStorage", JSON.stringify(syncParams), StorageManager.Fixed); + }) } - decryptStorage() { + async decryptStorage() { var stored = JSON.parse(this.getItem("encryptedStorage", StorageManager.Fixed)); - SFItemTransformer.decryptItem(stored, this.encryptedStorageKeys); + await SFJS.itemTransformer.decryptItem(stored, this.encryptedStorageKeys); var encryptedStorage = new EncryptedStorage(stored); for(var key of Object.keys(encryptedStorage.storage)) { diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index 610d81da7..fc5076e4f 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -29,24 +29,26 @@ class SyncManager { var version = this.authManager.offline() ? this.passcodeManager.protocolVersion() : this.authManager.protocolVersion(); var keys = this.authManager.offline() ? this.passcodeManager.keys() : this.authManager.keys(); - var params = items.map(function(item) { + + Promise.all(items.map(async (item) => { var itemParams = new ItemParams(item, keys, version); - itemParams = itemParams.paramsForLocalStorage(); + itemParams = await itemParams.paramsForLocalStorage(); if(offlineOnly) { delete itemParams.dirty; } return itemParams; - }.bind(this)); - - this.storageManager.saveModels(params, callback); + })).then((params) => { + this.storageManager.saveModels(params, callback); + }) } loadLocalItems(callback) { - var params = this.storageManager.getAllModels(function(items){ - var items = this.handleItemsResponse(items, null, ModelManager.MappingSourceLocalRetrieved); - Item.sortItemsByDate(items); - callback(items); - }.bind(this)) + var params = this.storageManager.getAllModels((items) => { + this.handleItemsResponse(items, null, ModelManager.MappingSourceLocalRetrieved).then((items) => { + Item.sortItemsByDate(items); + callback(items); + }) + }) } syncOffline(items, callback) { @@ -54,7 +56,7 @@ class SyncManager { for(var item of items) { item.updated_at = new Date(); } - this.writeItemsToLocalStorage(items, true, function(responseItems){ + this.writeItemsToLocalStorage(items, true, (responseItems) => { // delete anything needing to be deleted for(var item of items) { if(item.deleted) { @@ -70,7 +72,7 @@ class SyncManager { if(callback) { callback({success: true}); } - }.bind(this)) + }) } @@ -201,7 +203,7 @@ class SyncManager { this.syncLocked = false; } - sync(callback, options = {}, source) { + async sync(callback, options = {}, source) { if(this.syncLocked) { console.log("Sync Locked, Returning;"); @@ -281,11 +283,14 @@ class SyncManager { var params = {}; params.limit = 150; - params.items = _.map(subItems, function(item){ + + await Promise.all(subItems.map((item) => { var itemParams = new ItemParams(item, keys, version); itemParams.additionalFields = options.additionalFields; return itemParams.paramsForSync(); - }.bind(this)); + })).then((itemsParams) => { + params.items = itemsParams; + }) for(var item of subItems) { // Reset dirty counter to 0, since we're about to sync it. @@ -300,7 +305,7 @@ class SyncManager { this.stopCheckingIfSyncIsTakingTooLong(); }.bind(this); - var onSyncSuccess = function(response) { + var onSyncSuccess = async function(response) { // Check to make sure any subItem hasn't been marked as dirty again while a sync was ongoing var itemsToClearAsDirty = []; for(var item of subItems) { @@ -325,8 +330,7 @@ class SyncManager { // Map retrieved items to local data // Note that deleted items will not be returned - var retrieved - = this.handleItemsResponse(response.retrieved_items, null, ModelManager.MappingSourceRemoteRetrieved); + var retrieved = await this.handleItemsResponse(response.retrieved_items, null, ModelManager.MappingSourceRemoteRetrieved); // Append items to master list of retrieved items for this ongoing sync operation this.allRetreivedItems = this.allRetreivedItems.concat(retrieved); @@ -337,8 +341,7 @@ class SyncManager { var omitFields = ["content", "auth_hash"]; // Map saved items to local data - var saved = - this.handleItemsResponse(response.saved_items, omitFields, ModelManager.MappingSourceRemoteSaved); + var saved = await this.handleItemsResponse(response.saved_items, omitFields, ModelManager.MappingSourceRemoteSaved); // Append items to master list of saved items for this ongoing sync operation this.allSavedItems = this.allSavedItems.concat(saved); @@ -418,9 +421,9 @@ class SyncManager { } } - handleItemsResponse(responseItems, omitFields, source) { + async handleItemsResponse(responseItems, omitFields, source) { var keys = this.authManager.keys() || this.passcodeManager.keys(); - SFItemTransformer.decryptMultipleItems(responseItems, keys); + await SFJS.itemTransformer.decryptMultipleItems(responseItems, keys); var items = this.modelManager.mapResponseItemsToLocalModelsOmittingFields(responseItems, omitFields, source); // During the decryption process, items may be marked as "errorDecrypting". If so, we want to be sure @@ -446,7 +449,7 @@ class SyncManager { } } - handleUnsavedItemsResponse(unsaved) { + async handleUnsavedItemsResponse(unsaved) { if(unsaved.length == 0) { return; } @@ -454,7 +457,7 @@ class SyncManager { console.log("Handle unsaved", unsaved); var i = 0; - var handleNext = () => { + var handleNext = async () => { if(i >= unsaved.length) { // Handled all items this.sync(null, {additionalFields: ["created_at", "updated_at"]}); @@ -463,7 +466,7 @@ class SyncManager { var mapping = unsaved[i]; var itemResponse = mapping.item; - SFItemTransformer.decryptMultipleItems([itemResponse], this.authManager.keys()); + await SFJS.itemTransformer.decryptMultipleItems([itemResponse], this.authManager.keys()); var item = this.modelManager.findItem(itemResponse.uuid); if(!item) { diff --git a/package-lock.json b/package-lock.json index 78566d128..cb45c24cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1001,6 +1001,15 @@ "regenerator-transform": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz" } }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, "babel-plugin-transform-strict-mode": { "version": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", diff --git a/package.json b/package.json index 45b302d31..32d43b9c9 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "angular": "^1.6.1", "angular-mocks": "^1.6.1", "babel-cli": "^6.18.0", + "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.1.1", "babel-preset-es2016": "^6.16.0", "bower": "^1.8.0",