From 367fbeed20667c8103b22d9478d8041d432fe775 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Sun, 20 May 2018 12:23:09 -0500 Subject: [PATCH] Strict sign in --- .../javascripts/app/controllers/home.js | 2 +- .../app/directives/views/accountMenu.js | 9 ++- .../app/directives/views/passwordWizard.js | 41 +++++++------ .../javascripts/app/services/authManager.js | 61 ++++++++++++------- app/assets/stylesheets/app/_modals.scss | 2 +- app/assets/stylesheets/app/_stylekit-sub.scss | 19 ++++++ .../directives/account-menu.html.haml | 14 ++++- 7 files changed, 100 insertions(+), 48 deletions(-) diff --git a/app/assets/javascripts/app/controllers/home.js b/app/assets/javascripts/app/controllers/home.js index a1b153809..7bd300fd2 100644 --- a/app/assets/javascripts/app/controllers/home.js +++ b/app/assets/javascripts/app/controllers/home.js @@ -286,7 +286,7 @@ angular.module('app') }) } } else { - authManager.login(server, email, pw, false, {}, function(response){ + authManager.login(server, email, pw, false, false, {}, function(response){ window.location.reload(); }) } diff --git a/app/assets/javascripts/app/directives/views/accountMenu.js b/app/assets/javascripts/app/directives/views/accountMenu.js index a743309bc..22171a601 100644 --- a/app/assets/javascripts/app/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/directives/views/accountMenu.js @@ -17,10 +17,6 @@ class AccountMenu { $scope.user = authManager.user; $scope.server = syncManager.serverURL; - // $timeout(() => { - // $scope.openPasswordWizard("change-pw"); - // }, 0) - $scope.close = function() { $timeout(() => { $scope.closeFunction()(); @@ -61,10 +57,13 @@ class AccountMenu { $scope.formData.status = "Generating Login Keys..."; $timeout(function(){ - authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, $scope.formData.ephemeral, extraParams, + authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, + $scope.formData.ephemeral, $scope.formData.strictSignin, extraParams, (response) => { if(!response || response.error) { + syncManager.unlockSyncing(); + $scope.formData.status = null; var error = response ? response.error : {message: "An unknown error occured."} diff --git a/app/assets/javascripts/app/directives/views/passwordWizard.js b/app/assets/javascripts/app/directives/views/passwordWizard.js index 9669450b7..b184cb9c2 100644 --- a/app/assets/javascripts/app/directives/views/passwordWizard.js +++ b/app/assets/javascripts/app/directives/views/passwordWizard.js @@ -114,12 +114,15 @@ class PasswordWizard { $scope.formData.processing = true; $scope.processPasswordChange((passwordSuccess) => { - $scope.formData.statusError = $scope.formData.processing = !passwordSuccess; + $scope.formData.statusError = !passwordSuccess; + $scope.formData.processing = passwordSuccess; + if(passwordSuccess) { $scope.formData.status = "Encrypting data with new keys..."; $scope.resyncData((syncSuccess) => { - $scope.formData.statusError = $scope.formData.processing = !syncSuccess; + $scope.formData.statusError = !syncSuccess; + $scope.formData.processing = syncSuccess; if(syncSuccess) { $scope.lockContinue = false; @@ -173,7 +176,9 @@ class PasswordWizard { let password = $scope.formData.currentPassword; SFJS.crypto.computeEncryptionKeysForUser(password, authParams, (keys) => { let success = keys.mk === authManager.keys().mk; - if(!success) { + if(success) { + this.currentServerPw = keys.pw; + } else { alert("The current password you entered is not correct. Please try again."); } $timeout(() => callback(success)); @@ -193,22 +198,24 @@ class PasswordWizard { } $scope.processPasswordChange = function(callback) { - let currentPassword = $scope.formData.currentPassword; - let newPass = $scope.securityUpdate ? currentPassword : $scope.formData.newPassword; + let newUserPassword = $scope.securityUpdate ? $scope.formData.currentPassword : $scope.formData.newPassword; - // 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(currentPassword, newPass, (response) => { - if(response.error) { - alert("There was an error changing your password. Please try again."); - $timeout(() => callback(false)); - } else { - $timeout(() => callback(true)); - } - }) - }, null, "submitPasswordChange") + let currentServerPw = this.currentServerPw; + + SFJS.crypto.generateInitialEncryptionKeysForUser(authManager.user.email, newUserPassword, (newKeys, 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) => { + if(response.error) { + alert(response.error.message ? response.error.message : "There was an error changing your password. Please try again."); + $timeout(() => callback(false)); + } else { + $timeout(() => callback(true)); + } + }) + }, null, "submitPasswordChange") + }); } - } } diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js index 90d1db941..f1ed01afb 100644 --- a/app/assets/javascripts/app/services/authManager.js +++ b/app/assets/javascripts/app/services/authManager.js @@ -97,7 +97,19 @@ angular.module('app') understanding they are signing in with an older version of the protocol, and must upgrade immediately after completing sign in. */ this.isProtocolVersionOutdated = function(version) { - return ["001"].includes(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) { @@ -117,7 +129,7 @@ angular.module('app') }) } - this.login = function(url, email, password, ephemeral, extraParams, callback) { + this.login = function(url, email, password, ephemeral, strictSignin, extraParams, callback) { this.getAuthParamsForEmail(url, email, extraParams, function(authParams){ // SF3 requires a unique identifier in the auth params @@ -147,7 +159,7 @@ angular.module('app') } if(this.isProtocolVersionOutdated(authParams.version)) { - let message = `The encryption version for your account, ${authParams.version}, is outdated. 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.` + 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; } @@ -168,21 +180,30 @@ angular.module('app') return; } + if(strictSignin) { + // Refuse sign in if authParams.version is anything but the latest version + var latestVersion = SFJS.crypto.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, function(keys){ - console.log("Signing in with params", authParams, keys); var requestUrl = url + "/auth/sign_in"; var params = _.merge({password: keys.pw, email: email}, extraParams); httpManager.postAbsolute(requestUrl, params, function(response){ this.setEphemeral(ephemeral); this.handleAuthResponse(response, email, url, authParams, keys); this.checkForSecurityUpdate(); - callback(response); + $timeout(() => callback(response)); }.bind(this), function(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."}}; } - callback(response); + $timeout(() => callback(response)); }) }.bind(this)); @@ -235,23 +256,21 @@ angular.module('app') }); } - this.changePassword = function(current_password, new_password, callback) { + this.changePassword = function(current_server_pw, newKeys, newAuthParams, callback) { let email = this.user.email; - SFJS.crypto.generateInitialEncryptionKeysForUser(email, new_password, (keys, authParams) => { - var requestUrl = storageManager.getItem("server") + "/auth/change_pw"; - var params = _.merge({current_password: current_password, new_password: keys.pw}, authParams); + let newServerPw = newKeys.pw; - httpManager.postAbsolute(requestUrl, params, (response) => { - this.handleAuthResponse(response, email, null, authParams, keys); - callback(response); - }, (response) => { - var error = response; - if(!error) { - error = {message: "Something went wrong while changing your password. Your password was not changed. Please try again."} - } - console.error("Change pw error", response); - callback({error: error}); - }) + var requestUrl = storageManager.getItem("server") + "/auth/change_pw"; + var params = _.merge({new_password: newServerPw}, newAuthParams); + + httpManager.postAbsolute(requestUrl, params, (response) => { + this.handleAuthResponse(response, email, null, newAuthParams, newKeys); + callback(response); + }, (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); }) } diff --git a/app/assets/stylesheets/app/_modals.scss b/app/assets/stylesheets/app/_modals.scss index 29171da36..e8ad7fa99 100644 --- a/app/assets/stylesheets/app/_modals.scss +++ b/app/assets/stylesheets/app/_modals.scss @@ -54,7 +54,7 @@ &.small { > .content { width: 700px; - height: 335px; + height: 344px; } } diff --git a/app/assets/stylesheets/app/_stylekit-sub.scss b/app/assets/stylesheets/app/_stylekit-sub.scss index 5fb32e5bc..cd183fcc4 100644 --- a/app/assets/stylesheets/app/_stylekit-sub.scss +++ b/app/assets/stylesheets/app/_stylekit-sub.scss @@ -1,5 +1,24 @@ .sn-component { + .notification { + &.unpadded { + padding: 0; + padding-bottom: 0 !important; + padding-top: 0; + } + + .padded-row { + padding: 10px 12px; + } + + .bordered-row { + border-bottom: 1px solid rgba(black, 0.1); + border-top: 1px solid rgba(black, 0.1); + } + } + + + } .panel { diff --git a/app/assets/templates/directives/account-menu.html.haml b/app/assets/templates/directives/account-menu.html.haml index 093fdc6b9..d5e949619 100644 --- a/app/assets/templates/directives/account-menu.html.haml +++ b/app/assets/templates/directives/account-menu.html.haml @@ -31,10 +31,18 @@ .notification.info{"ng-if" => "formData.showRegister"} %h2.title No Password Reset. .text Because your notes are encrypted using your password, Standard Notes does not have a password reset option. You cannot forget your password. - .advanced-options.panel-row{"ng-if" => "formData.showAdvanced"} + + .notification.unpadded.default.advanced-options.panel-row{"ng-if" => "formData.showAdvanced"} .panel-column.stretch - %label Sync Server Domain - %input.form-control.mt-5{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'formData.url'} + %h4.title.panel-row.padded-row Advanced Options + %div.bordered-row.padded-row + %label Sync Server Domain + %input.form-control.mt-5{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'formData.url'} + %label.padded-row{"ng-if" => "formData.showLogin"} + %input{"type" => "checkbox", "ng-model" => "formData.strictSignin"} + Use strict sign in + %span + %a{"href" => "https://standardnotes.org/help/security", "target" => "_blank"} (Learn more) .button-group.stretch.panel-row.form-submit %button.button.info.featured{"type" => "submit"}