From 39062f17fd4307713cc25fc42bc1f302f1e9bdc9 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 28 Jun 2018 11:44:23 -0500 Subject: [PATCH] Refactor auth manager and async storage manager --- .../javascripts/app/controllers/home.js | 8 +- .../app/directives/views/passwordWizard.js | 8 +- .../app/services/actionsManager.js | 8 +- .../app/services/archiveManager.js | 6 +- .../javascripts/app/services/authManager.js | 509 +++++++----------- .../app/services/desktopManager.js | 6 +- .../app/services/old_authManager.js | 326 +++++++++++ .../app/services/passcodeManager.js | 4 +- .../app/services/storageManager.js | 74 +-- 9 files changed, 576 insertions(+), 373 deletions(-) create mode 100644 app/assets/javascripts/app/services/old_authManager.js diff --git a/app/assets/javascripts/app/controllers/home.js b/app/assets/javascripts/app/controllers/home.js index ba42c4789..ec0f7c1c6 100644 --- a/app/assets/javascripts/app/controllers/home.js +++ b/app/assets/javascripts/app/controllers/home.js @@ -34,7 +34,7 @@ angular.module('app') window.location.reload(); } - function load() { + function async load() { // pass keys to storageManager to decrypt storage // Update: Wait, why? passcodeManager already handles this. // storageManager.setKeys(passcodeManager.keys()); @@ -73,10 +73,10 @@ angular.module('app') function initiateSync() { authManager.loadInitialData(); - syncManager.setKeyRequestHandler(() => { + syncManager.setKeyRequestHandler(async () => { let offline = authManager.offline(); - let version = offline ? passcodeManager.protocolVersion() : authManager.protocolVersion(); - let keys = offline ? passcodeManager.keys() : authManager.keys(); + let version = offline ? passcodeManager.protocolVersion() : await authManager.protocolVersion(); + let keys = offline ? passcodeManager.keys() : await authManager.keys(); return { keys: keys, offline: offline, diff --git a/app/assets/javascripts/app/directives/views/passwordWizard.js b/app/assets/javascripts/app/directives/views/passwordWizard.js index 3b5024046..430d6ff4c 100644 --- a/app/assets/javascripts/app/directives/views/passwordWizard.js +++ b/app/assets/javascripts/app/directives/views/passwordWizard.js @@ -147,7 +147,7 @@ class PasswordWizard { } } - $scope.validateCurrentPassword = function(callback) { + $scope.validateCurrentPassword = async function(callback) { let currentPassword = $scope.formData.currentPassword; let newPass = $scope.securityUpdate ? currentPassword : $scope.formData.newPassword; @@ -175,8 +175,8 @@ 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).then((keys) => { - let success = keys.mk === authManager.keys().mk; + SFJS.crypto.computeEncryptionKeysForUser(password, authParams).then(async (keys) => { + let success = keys.mk === (await authManager.keys()).mk; if(success) { this.currentServerPw = keys.pw; } else { @@ -209,7 +209,7 @@ class PasswordWizard { // 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) => { + authManager.changePassword(authManager.user.email, currentServerPw, newKeys, newAuthParams, (response) => { if(response.error) { alert(response.error.message ? response.error.message : "There was an error changing your password. Please try again."); $timeout(() => callback(false)); diff --git a/app/assets/javascripts/app/services/actionsManager.js b/app/assets/javascripts/app/services/actionsManager.js index fb283a912..ef626e97f 100644 --- a/app/assets/javascripts/app/services/actionsManager.js +++ b/app/assets/javascripts/app/services/actionsManager.js @@ -124,7 +124,7 @@ class ActionsManager { case "get": { this.httpManager.getAbsolute(action.url, {}, (response) => { action.error = false; - handleResponseDecryption(response, this.authManager.keys(), true); + handleResponseDecryption(response, await this.authManager.keys(), true); }, (response) => { action.error = true; customCallback(null); @@ -136,7 +136,7 @@ class ActionsManager { this.httpManager.getAbsolute(action.url, {}, (response) => { action.error = false; - handleResponseDecryption(response, this.authManager.keys(), false); + handleResponseDecryption(response, await this.authManager.keys(), false); }, (response) => { action.error = true; customCallback(null); @@ -175,11 +175,11 @@ class ActionsManager { } async outgoingParamsForItem(item, extension, decrypted = false) { - var keys = this.authManager.keys(); + var keys = await this.authManager.keys(); if(decrypted) { keys = null; } - var itemParams = new SFItemParams(item, keys, this.authManager.protocolVersion()); + var itemParams = new SFItemParams(item, keys, await this.authManager.protocolVersion()); return itemParams.paramsForExtension(); } diff --git a/app/assets/javascripts/app/services/archiveManager.js b/app/assets/javascripts/app/services/archiveManager.js index 638541afe..ccf18e8ad 100644 --- a/app/assets/javascripts/app/services/archiveManager.js +++ b/app/assets/javascripts/app/services/archiveManager.js @@ -10,7 +10,7 @@ class ArchiveManager { Public */ - downloadBackup(encrypted) { + async downloadBackup(encrypted) { // download in Standard File format var keys, authParams, protocolVersion; if(encrypted) { @@ -19,9 +19,9 @@ class ArchiveManager { authParams = this.passcodeManager.passcodeAuthParams(); protocolVersion = authParams.version; } else { - keys = this.authManager.keys(); + keys = await this.authManager.keys(); authParams = this.authManager.getAuthParams(); - protocolVersion = this.authManager.protocolVersion(); + protocolVersion = await this.authManager.protocolVersion(); } } this.__itemsData(keys, authParams, protocolVersion).then((data) => { diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js index 49793b140..4c6afb0e7 100644 --- a/app/assets/javascripts/app/services/authManager.js +++ b/app/assets/javascripts/app/services/authManager.js @@ -1,330 +1,203 @@ -angular.module('app') - .provider('authManager', function () { +class AuthManager extends SFAuthManager { - function domainName() { - var domain_comps = location.hostname.split("."); - var domain = domain_comps[domain_comps.length - 2] + "." + domain_comps[domain_comps.length - 1]; - return domain; + constructor(modelManager, singletonManager, storageManager, dbManager, httpManager, $rootScope, $timeout, $compile) { + super(storageManager, httpManager, $timeout); + this.$rootScope = $rootScope; + this.$compile = $compile; + this.modelManager = modelManager; + this.singletonManager = singletonManager; + this.storageManager = storageManager; + this.dbManager = dbManager; + } + + loadInitialData() { + var userData = this.storageManager.getItemSync("user"); + if(userData) { + this.user = JSON.parse(userData); + } else { + // legacy, check for uuid + var idData = this.storageManager.getItemSync("uuid"); + if(idData) { + this.user = {uuid: idData}; + } } - this.$get = function($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile) { - return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile); + this.configureUserPrefs(); + this.checkForSecurityUpdate(); + } + + offline() { + return !this.user; + } + + isEphemeralSession() { + if(this.ephemeral == null || this.ephemeral == undefined) { + this.ephemeral = JSON.parse(this.storageManager.getItemSync("ephemeral", StorageManager.Fixed)); + } + return this.ephemeral; + } + + setEphemeral(ephemeral) { + this.ephemeral = ephemeral; + if(ephemeral) { + this.storageManager.setModelStorageMode(StorageManager.Ephemeral); + this.storageManager.setItemsMode(StorageManager.Ephemeral); + } else { + this.storageManager.setModelStorageMode(StorageManager.Fixed); + this.storageManager.setItemsMode(this.storageManager.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Fixed); + this.storageManager.setItem("ephemeral", JSON.stringify(false), StorageManager.Fixed); + } + } + + getAuthParams() { + if(!this._authParams) { + this._authParams = JSON.parse(this.storageManager.getItemSync("auth_params")); + } + return this._authParams; + } + + keys() { + if(!this._keys) { + var mk = this.storageManager.getItemSync("mk"); + if(!mk) { + return null; + } + this._keys = {mk: mk, ak: this.storageManager.getItemSync("ak")}; + } + return this._keys; + } + + async protocolVersion() { + var authParams = this.getAuthParams(); + if(authParams && authParams.version) { + return authParams.version; } - function AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile) { + var keys = await this.keys(); + if(keys && keys.ak) { + // If there's no version stored, and there's an ak, it has to be 002. Newer versions would have thier version stored in authParams. + return "002"; + } else { + return "001"; + } + } - this.loadInitialData = function() { - var userData = storageManager.getItem("user"); - if(userData) { - this.user = JSON.parse(userData); - } else { - // legacy, check for uuid - var idData = storageManager.getItem("uuid"); - if(idData) { - this.user = {uuid: idData}; - } - } + getAuthParamsForEmail(url, email, extraParams, callback) { + super.getAuthParamsForEmail(url, email, extraParams, (response) => { + callback(response); + }) + } + login(url, email, password, ephemeral, strictSignin, extraParams, callback) { + super.login(url, email, password, ephemeral, strictSignin, extraParams, (response) => { + if(!response.error) { + this.setEphemeral(ephemeral); + this.handleAuthResponse(response, email, url, authParams, keys); this.checkForSecurityUpdate(); } + this.$timeout(() => callback(response)); + }) + } - this.offline = function() { - return !this.user; - } - - this.isEphemeralSession = function() { - if(this.ephemeral == null || this.ephemeral == undefined) { - this.ephemeral = JSON.parse(storageManager.getItem("ephemeral", StorageManager.Fixed)); - } - return this.ephemeral; - } - - this.setEphemeral = function(ephemeral) { - this.ephemeral = ephemeral; - if(ephemeral) { - storageManager.setModelStorageMode(StorageManager.Ephemeral); - storageManager.setItemsMode(StorageManager.Ephemeral); - } else { - storageManager.setModelStorageMode(StorageManager.Fixed); - storageManager.setItemsMode(storageManager.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Fixed); - storageManager.setItem("ephemeral", JSON.stringify(false), StorageManager.Fixed); - } - } - - this.getAuthParams = function() { - if(!this._authParams) { - this._authParams = JSON.parse(storageManager.getItem("auth_params")); - } - return this._authParams; - } - - this.keys = function() { - if(!this._keys) { - var mk = storageManager.getItem("mk"); - if(!mk) { - return null; - } - this._keys = {mk: mk, ak: storageManager.getItem("ak")}; - } - return this._keys; - } - - this.protocolVersion = function() { - var authParams = this.getAuthParams(); - if(authParams && authParams.version) { - return authParams.version; - } - - var keys = this.keys(); - if(keys && keys.ak) { - // If there's no version stored, and there's an ak, it has to be 002. Newer versions would have thier version stored in authParams. - return "002"; - } else { - return "001"; - } - } - - this.isProtocolVersionSupported = function(version) { - return SFJS.supportedVersions().includes(version); - } - - this.getAuthParamsForEmail = function(url, email, extraParams, callback) { - var requestUrl = url + "/auth/params"; - httpManager.getAbsolute(requestUrl, _.merge({email: email}, extraParams), function(response){ - callback(response); - }, function(response){ - console.error("Error getting auth params", response); - if(typeof response !== 'object') { - response = {error: {message: "A server error occurred while trying to sign in. Please try again."}}; - } - callback(response); - }) - } - - this.login = function(url, email, password, ephemeral, strictSignin, extraParams, callback) { - this.getAuthParamsForEmail(url, email, extraParams, (authParams) => { - - // SF3 requires a unique identifier in the auth params - authParams.identifier = email; - - if(authParams.error) { - callback(authParams); - return; - } - - if(!authParams || !authParams.pw_cost) { - callback({error : {message: "Invalid email or password."}}); - return; - } - - if(!this.isProtocolVersionSupported(authParams.version)) { - var message; - 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 { - // The user has a very old account type, which is no longer supported by this client - message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security for more information."; - } - callback({error: {message: message}}); - return; - } - - 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)) { - callback({error: {}}); - return; - } - } - - 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." - callback({error: {message: message}}); - return; - } - - 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}}); - return; - } - - if(strictSignin) { - // Refuse sign in if authParams.version is anything but the latest 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}}); - return; - } - } - - 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, (response) => { - this.setEphemeral(ephemeral); - this.handleAuthResponse(response, email, url, authParams, keys); - this.checkForSecurityUpdate(); - $timeout(() => callback(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)); - }); - - }); - }) - } - - this.handleAuthResponse = function(response, email, url, authParams, keys) { - try { - if(url) { - storageManager.setItem("server", url); - } - - this.user = response.user; - storageManager.setItem("user", JSON.stringify(response.user)); - - this._authParams = authParams; - storageManager.setItem("auth_params", JSON.stringify(authParams)); - - storageManager.setItem("jwt", response.token); - this.saveKeys(keys); - } catch(e) { - dbManager.displayOfflineAlert(); - } - } - - this.saveKeys = function(keys) { - this._keys = keys; - // pw doesn't need to be saved. - // storageManager.setItem("pw", keys.pw); - storageManager.setItem("mk", keys.mk); - storageManager.setItem("ak", keys.ak); - } - - this.register = function(url, email, password, ephemeral, callback) { - SFJS.crypto.generateInitialKeysAndAuthParamsForUser(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); - - httpManager.postAbsolute(requestUrl, params, (response) => { - this.setEphemeral(ephemeral); - this.handleAuthResponse(response, email, url, authParams, keys); - callback(response); - }, (response) => { - console.error("Registration error", response); - if(typeof response !== 'object') { - response = {error: {message: "A server error occurred while trying to register. Please try again."}}; - } - callback(response); - }) - }); - } - - this.changePassword = function(current_server_pw, newKeys, newAuthParams, callback) { - let email = this.user.email; - let newServerPw = newKeys.pw; - - var requestUrl = storageManager.getItem("server") + "/auth/change_pw"; - var params = _.merge({new_password: newServerPw, current_password: current_server_pw}, newAuthParams); - - httpManager.postAbsolute(requestUrl, params, (response) => { - this.handleAuthResponse(response, email, null, newAuthParams, newKeys); - callback(response); - - // Allows security update status to be changed if neccessary - this.checkForSecurityUpdate(); - }, (response) => { - if(typeof response !== 'object') { - response = {error: {message: "Something went wrong while changing your password. Your password was not changed. Please try again."}} - } - callback(response); - }) - } - - this.checkForSecurityUpdate = function() { - if(this.offline()) { - return false; - } - - let latest = SFJS.version(); - let updateAvailable = this.protocolVersion() !== latest; - if(updateAvailable !== this.securityUpdateAvailable) { - this.securityUpdateAvailable = updateAvailable; - $rootScope.$broadcast("security-update-status-changed"); - } - - return this.securityUpdateAvailable; - } - - this.presentPasswordWizard = function(type) { - var scope = $rootScope.$new(true); - scope.type = type; - var el = $compile( "" )(scope); - angular.element(document.body).append(el); - } - - this.staticifyObject = function(object) { - return JSON.parse(JSON.stringify(object)); - } - - this.signOut = function() { - this._keys = null; - this.user = null; - this._authParams = null; - } - - - /* User Preferences */ - - let prefsContentType = "SN|UserPreferences"; - - singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => { - this.userPreferences = resolvedSingleton; - this.userPreferencesDidChange(); - }, (valueCallback) => { - // Safe to create. Create and return object. - var prefs = new SFItem({content_type: prefsContentType}); - modelManager.addItem(prefs); - prefs.setDirty(true); - $rootScope.sync("authManager singletonCreate"); - valueCallback(prefs); - }); - - this.userPreferencesDidChange = function() { - $rootScope.$broadcast("user-preferences-changed"); - } - - this.syncUserPreferences = function() { - this.userPreferences.setDirty(true); - $rootScope.sync("syncUserPreferences"); - } - - this.getUserPrefValue = function(key, defaultValue) { - if(!this.userPreferences) { return defaultValue; } - var value = this.userPreferences.getAppDataItem(key); - return (value !== undefined && value != null) ? value : defaultValue; - } - - this.setUserPrefValue = function(key, value, sync) { - if(!this.userPreferences) { console.log("Prefs are null, not setting value", key); return; } - this.userPreferences.setAppDataItem(key, value); - if(sync) { - this.syncUserPreferences(); - } - } - + handleAuthResponse(response, email, url, authParams, keys) { + try { + super.handleAuthResponse(response, email, url, authParams, keys); + this._authParams = authParams; + this.user = response.user; + this.storageManager.setItem("user", JSON.stringify(response.user)); + } catch (e) { + this.dbManager.displayOfflineAlert(); } + } + + register(url, email, password, ephemeral, callback) { + super.register(url, email, password, ephemeral, (response) => { + if(!response.error) { + this.setEphemeral(ephemeral); + } + callback(response); + }) + } + + changePassword(email, current_server_pw, newKeys, newAuthParams, callback) { + super.changePassword(email, current_server_pw, newKeys, newAuthParams, (response) => { + if(!response.error) { + // Allows security update status to be changed if neccessary + this.checkForSecurityUpdate(); + } + callback(response); + }) + } + + checkForSecurityUpdate() { + if(this.offline()) { + return false; + } + + let latest = SFJS.version(); + let updateAvailable = this.protocolVersion() !== latest; + if(updateAvailable !== this.securityUpdateAvailable) { + this.securityUpdateAvailable = updateAvailable; + this.$rootScope.$broadcast("security-update-status-changed"); + } + + return this.securityUpdateAvailable; + } + + presentPasswordWizard(type) { + var scope = this.$rootScope.$new(true); + scope.type = type; + var el = this.$compile( "" )(scope); + angular.element(document.body).append(el); + } + + signOut() { + this._keys = null; + this.user = null; + this._authParams = null; + } + + + /* User Preferences */ + + configureUserPrefs() { + let prefsContentType = "SN|UserPreferences"; + + this.singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => { + this.userPreferences = resolvedSingleton; + this.userPreferencesDidChange(); + }, (valueCallback) => { + // Safe to create. Create and return object. + var prefs = new SFItem({content_type: prefsContentType}); + this.modelManager.addItem(prefs); + prefs.setDirty(true); + this.$rootScope.sync("authManager singletonCreate"); + valueCallback(prefs); + }); + } + + userPreferencesDidChange() { + this.$rootScope.$broadcast("user-preferences-changed"); + } + + syncUserPreferences() { + this.userPreferences.setDirty(true); + this.$rootScope.sync("syncUserPreferences"); + } + + getUserPrefValue(key, defaultValue) { + if(!this.userPreferences) { return defaultValue; } + var value = this.userPreferences.getAppDataItem(key); + return (value !== undefined && value != null) ? value : defaultValue; + } + + setUserPrefValue(key, value, sync) { + if(!this.userPreferences) { console.log("Prefs are null, not setting value", key); return; } + this.userPreferences.setAppDataItem(key, value); + if(sync) { + this.syncUserPreferences(); + } + } }); + +angular.module('app').service('authManager', AuthManager); diff --git a/app/assets/javascripts/app/services/desktopManager.js b/app/assets/javascripts/app/services/desktopManager.js index a643127a7..31f77f5dc 100644 --- a/app/assets/javascripts/app/services/desktopManager.js +++ b/app/assets/javascripts/app/services/desktopManager.js @@ -129,16 +129,16 @@ class DesktopManager { } } - desktop_requestBackupFile(callback) { + async desktop_requestBackupFile(callback) { var keys, authParams, protocolVersion; if(this.authManager.offline() && this.passcodeManager.hasPasscode()) { keys = this.passcodeManager.keys(); authParams = this.passcodeManager.passcodeAuthParams(); protocolVersion = authParams.version; } else { - keys = this.authManager.keys(); + keys = await this.authManager.keys(); authParams = this.authManager.getAuthParams(); - protocolVersion = this.authManager.protocolVersion(); + protocolVersion = await this.authManager.protocolVersion(); } this.modelManager.getAllItemsJSONData( diff --git a/app/assets/javascripts/app/services/old_authManager.js b/app/assets/javascripts/app/services/old_authManager.js new file mode 100644 index 000000000..4fd9eaf7d --- /dev/null +++ b/app/assets/javascripts/app/services/old_authManager.js @@ -0,0 +1,326 @@ +// angular.module('app') +// .provider('authManager', function () { +// +// function domainName() { +// var domain_comps = location.hostname.split("."); +// var domain = domain_comps[domain_comps.length - 2] + "." + domain_comps[domain_comps.length - 1]; +// return domain; +// } +// +// this.$get = function($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile) { +// return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile); +// } +// +// function AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager, $compile) { +// +// this.loadInitialData = function() { +// var userData = storageManager.getItem("user"); +// if(userData) { +// this.user = JSON.parse(userData); +// } else { +// // legacy, check for uuid +// var idData = storageManager.getItem("uuid"); +// if(idData) { +// this.user = {uuid: idData}; +// } +// } +// +// this.checkForSecurityUpdate(); +// } +// +// this.offline = function() { +// return !this.user; +// } +// +// this.isEphemeralSession = function() { +// if(this.ephemeral == null || this.ephemeral == undefined) { +// this.ephemeral = JSON.parse(storageManager.getItem("ephemeral", StorageManager.Fixed)); +// } +// return this.ephemeral; +// } +// +// this.setEphemeral = function(ephemeral) { +// this.ephemeral = ephemeral; +// if(ephemeral) { +// storageManager.setModelStorageMode(StorageManager.Ephemeral); +// storageManager.setItemsMode(StorageManager.Ephemeral); +// } else { +// storageManager.setModelStorageMode(StorageManager.Fixed); +// storageManager.setItemsMode(storageManager.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Fixed); +// storageManager.setItem("ephemeral", JSON.stringify(false), StorageManager.Fixed); +// } +// } +// +// this.getAuthParams = function() { +// if(!this._authParams) { +// this._authParams = JSON.parse(storageManager.getItem("auth_params")); +// } +// return this._authParams; +// } +// +// this.keys = function() { +// if(!this._keys) { +// var mk = storageManager.getItem("mk"); +// if(!mk) { +// return null; +// } +// this._keys = {mk: mk, ak: storageManager.getItem("ak")}; +// } +// return this._keys; +// } +// +// this.protocolVersion = function() { +// var authParams = this.getAuthParams(); +// if(authParams && authParams.version) { +// return authParams.version; +// } +// +// var keys = this.keys(); +// if(keys && keys.ak) { +// // If there's no version stored, and there's an ak, it has to be 002. Newer versions would have thier version stored in authParams. +// return "002"; +// } else { +// return "001"; +// } +// } +// +// this.getAuthParamsForEmail = function(url, email, extraParams, callback) { +// var requestUrl = url + "/auth/params"; +// httpManager.getAbsolute(requestUrl, _.merge({email: email}, extraParams), function(response){ +// callback(response); +// }, function(response){ +// console.error("Error getting auth params", response); +// if(typeof response !== 'object') { +// response = {error: {message: "A server error occurred while trying to sign in. Please try again."}}; +// } +// callback(response); +// }) +// } +// +// this.login = function(url, email, password, ephemeral, strictSignin, extraParams, callback) { +// this.getAuthParamsForEmail(url, email, extraParams, (authParams) => { +// +// // SF3 requires a unique identifier in the auth params +// authParams.identifier = email; +// +// if(authParams.error) { +// callback(authParams); +// return; +// } +// +// if(!authParams || !authParams.pw_cost) { +// callback({error : {message: "Invalid email or password."}}); +// return; +// } +// +// if(!SFJS.supportedVersions().includes(authParams.version)) { +// var message; +// 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 { +// // The user has a very old account type, which is no longer supported by this client +// message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security for more information."; +// } +// callback({error: {message: message}}); +// return; +// } +// +// 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)) { +// callback({error: {}}); +// return; +// } +// } +// +// 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." +// callback({error: {message: message}}); +// return; +// } +// +// 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}}); +// return; +// } +// +// if(strictSignin) { +// // Refuse sign in if authParams.version is anything but the latest 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}}); +// return; +// } +// } +// +// 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, (response) => { +// this.setEphemeral(ephemeral); +// this.handleAuthResponse(response, email, url, authParams, keys); +// this.checkForSecurityUpdate(); +// $timeout(() => callback(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)); +// }); +// +// }); +// }) +// } +// +// this.handleAuthResponse = function(response, email, url, authParams, keys) { +// try { +// if(url) { +// storageManager.setItem("server", url); +// } +// +// this.user = response.user; +// storageManager.setItem("user", JSON.stringify(response.user)); +// +// this._authParams = authParams; +// storageManager.setItem("auth_params", JSON.stringify(authParams)); +// +// storageManager.setItem("jwt", response.token); +// this.saveKeys(keys); +// } catch(e) { +// dbManager.displayOfflineAlert(); +// } +// } +// +// this.saveKeys = function(keys) { +// this._keys = keys; +// // pw doesn't need to be saved. +// // storageManager.setItem("pw", keys.pw); +// storageManager.setItem("mk", keys.mk); +// storageManager.setItem("ak", keys.ak); +// } +// +// this.register = function(url, email, password, ephemeral, callback) { +// SFJS.crypto.generateInitialKeysAndAuthParamsForUser(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); +// +// httpManager.postAbsolute(requestUrl, params, (response) => { +// this.setEphemeral(ephemeral); +// this.handleAuthResponse(response, email, url, authParams, keys); +// callback(response); +// }, (response) => { +// console.error("Registration error", response); +// if(typeof response !== 'object') { +// response = {error: {message: "A server error occurred while trying to register. Please try again."}}; +// } +// callback(response); +// }) +// }); +// } +// +// this.changePassword = function(current_server_pw, newKeys, newAuthParams, callback) { +// let email = this.user.email; +// let newServerPw = newKeys.pw; +// +// var requestUrl = storageManager.getItem("server") + "/auth/change_pw"; +// var params = _.merge({new_password: newServerPw, current_password: current_server_pw}, newAuthParams); +// +// httpManager.postAbsolute(requestUrl, params, (response) => { +// this.handleAuthResponse(response, email, null, newAuthParams, newKeys); +// callback(response); +// +// // Allows security update status to be changed if neccessary +// this.checkForSecurityUpdate(); +// }, (response) => { +// if(typeof response !== 'object') { +// response = {error: {message: "Something went wrong while changing your password. Your password was not changed. Please try again."}} +// } +// callback(response); +// }) +// } +// +// this.checkForSecurityUpdate = function() { +// if(this.offline()) { +// return false; +// } +// +// let latest = SFJS.version(); +// let updateAvailable = this.protocolVersion() !== latest; +// if(updateAvailable !== this.securityUpdateAvailable) { +// this.securityUpdateAvailable = updateAvailable; +// $rootScope.$broadcast("security-update-status-changed"); +// } +// +// return this.securityUpdateAvailable; +// } +// +// this.presentPasswordWizard = function(type) { +// var scope = $rootScope.$new(true); +// scope.type = type; +// var el = $compile( "" )(scope); +// angular.element(document.body).append(el); +// } +// +// this.staticifyObject = function(object) { +// return JSON.parse(JSON.stringify(object)); +// } +// +// this.signOut = function() { +// this._keys = null; +// this.user = null; +// this._authParams = null; +// } +// +// +// /* User Preferences */ +// +// let prefsContentType = "SN|UserPreferences"; +// +// singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => { +// this.userPreferences = resolvedSingleton; +// this.userPreferencesDidChange(); +// }, (valueCallback) => { +// // Safe to create. Create and return object. +// var prefs = new SFItem({content_type: prefsContentType}); +// modelManager.addItem(prefs); +// prefs.setDirty(true); +// $rootScope.sync("authManager singletonCreate"); +// valueCallback(prefs); +// }); +// +// this.userPreferencesDidChange = function() { +// $rootScope.$broadcast("user-preferences-changed"); +// } +// +// this.syncUserPreferences = function() { +// this.userPreferences.setDirty(true); +// $rootScope.sync("syncUserPreferences"); +// } +// +// this.getUserPrefValue = function(key, defaultValue) { +// if(!this.userPreferences) { return defaultValue; } +// var value = this.userPreferences.getAppDataItem(key); +// return (value !== undefined && value != null) ? value : defaultValue; +// } +// +// this.setUserPrefValue = function(key, value, sync) { +// if(!this.userPreferences) { console.log("Prefs are null, not setting value", key); return; } +// this.userPreferences.setAppDataItem(key, value); +// if(sync) { +// this.syncUserPreferences(); +// } +// } +// +// } +// }); diff --git a/app/assets/javascripts/app/services/passcodeManager.js b/app/assets/javascripts/app/services/passcodeManager.js index b30711ecc..587a61027 100644 --- a/app/assets/javascripts/app/services/passcodeManager.js +++ b/app/assets/javascripts/app/services/passcodeManager.js @@ -7,7 +7,7 @@ angular.module('app') function PasscodeManager($rootScope, $timeout, modelManager, dbManager, authManager, storageManager) { - this._hasPasscode = storageManager.getItem("offlineParams", StorageManager.Fixed) != null; + this._hasPasscode = storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null; this._locked = this._hasPasscode; this.isLocked = function() { @@ -23,7 +23,7 @@ angular.module('app') } this.passcodeAuthParams = function() { - return JSON.parse(storageManager.getItem("offlineParams", StorageManager.Fixed)); + return JSON.parse(storageManager.getItemSync("offlineParams", StorageManager.Fixed)); } this.protocolVersion = function() { diff --git a/app/assets/javascripts/app/services/storageManager.js b/app/assets/javascripts/app/services/storageManager.js index 681bf49fb..4f1ba26fb 100644 --- a/app/assets/javascripts/app/services/storageManager.js +++ b/app/assets/javascripts/app/services/storageManager.js @@ -101,7 +101,7 @@ class StorageManager extends SFStorageManager { } } - setItem(key, value, vaultKey) { + async setItem(key, value, vaultKey) { var storage = this.getVault(vaultKey); storage.setItem(key, value); @@ -110,25 +110,21 @@ class StorageManager extends SFStorageManager { } } - getItem(key, vault) { + async getItem(key, vault) { + return this.getItemSync(key, vault); + } + + getItemSync(key, vault) { var storage = this.getVault(vault); return storage.getItem(key); } - setBooleanValue(key, value, vault) { - this.setItem(key, JSON.stringify(value), vault); - } - - getBooleanValue(key, vault) { - return JSON.parse(this.getItem(key, vault)); - } - - removeItem(key, vault) { + async removeItem(key, vault) { var storage = this.getVault(vault); - storage.removeItem(key); + return storage.removeItem(key); } - clear() { + async clear() { this.memoryStorage.clear(); localStorage.clear(); } @@ -197,36 +193,44 @@ class StorageManager extends SFStorageManager { this.modelStorageMode = mode; } - getAllModels(callback) { - if(this.modelStorageMode == StorageManager.Fixed) { - this.dbManager.getAllModels(callback); - } else { - callback && callback(); - } + async getAllModels() { + return new Promise((resolve, reject) => { + if(this.modelStorageMode == StorageManager.Fixed) { + this.dbManager.getAllModels(resolve); + } else { + resolve(); + } + }) } - saveModel(item) { - this.saveModels([item]); + async saveModel(item) { + return this.saveModels([item]); } - saveModels(items, onsuccess, onerror) { - if(this.modelStorageMode == StorageManager.Fixed) { - this.dbManager.saveModels(items, onsuccess, onerror); - } else { - onsuccess && onsuccess(); - } + async saveModels(items, onsuccess, onerror) { + return new Promise((resolve, reject) => { + if(this.modelStorageMode == StorageManager.Fixed) { + this.dbManager.saveModels(items, resolve, reject); + } else { + resolve(); + } + }); } - deleteModel(item, callback) { - if(this.modelStorageMode == StorageManager.Fixed) { - this.dbManager.deleteModel(item, callback); - } else { - callback && callback(); - } + async deleteModel(item) { + return new Promise((resolve, reject) => { + if(this.modelStorageMode == StorageManager.Fixed) { + this.dbManager.deleteModel(item, resolve); + } else { + resolve(); + } + }); } - clearAllModels(callback) { - this.dbManager.clearAllModels(callback); + async clearAllModels() { + return new Promise((resolve, reject) => { + this.dbManager.clearAllModels(resolve); + }); } }