Actions handle error decrypting
This commit is contained in:
@@ -166,13 +166,4 @@ angular.module('app')
|
|||||||
this.selectRoom = function(room) {
|
this.selectRoom = function(room) {
|
||||||
room.showRoom = !room.showRoom;
|
room.showRoom = !room.showRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 002 Update
|
|
||||||
|
|
||||||
this.securityUpdateAvailable = function() {
|
|
||||||
var keys = authManager.keys()
|
|
||||||
return keys && !keys.ak;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
38
app/assets/javascripts/app/directives/views/inputModal.js
Normal file
38
app/assets/javascripts/app/directives/views/inputModal.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
class InputModal {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.restrict = "E";
|
||||||
|
this.templateUrl = "directives/input-modal.html";
|
||||||
|
this.scope = {
|
||||||
|
type: "=",
|
||||||
|
title: "=",
|
||||||
|
message: "=",
|
||||||
|
placeholder: "=",
|
||||||
|
callback: "&"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
link($scope, el, attrs) {
|
||||||
|
$scope.el = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller($scope, modelManager, archiveManager, authManager, syncManager, $timeout) {
|
||||||
|
'ngInject';
|
||||||
|
|
||||||
|
$scope.formData = {};
|
||||||
|
|
||||||
|
$scope.dismiss = function() {
|
||||||
|
$scope.el.remove();
|
||||||
|
$scope.$destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.submit = function() {
|
||||||
|
$scope.callback()($scope.formData.input);
|
||||||
|
$scope.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app').directive('inputModal', () => new InputModal);
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
class ActionsManager {
|
class ActionsManager {
|
||||||
|
|
||||||
constructor(httpManager, modelManager, authManager, syncManager) {
|
constructor(httpManager, modelManager, authManager, syncManager, $rootScope, $compile, $timeout) {
|
||||||
this.httpManager = httpManager;
|
this.httpManager = httpManager;
|
||||||
this.modelManager = modelManager;
|
this.modelManager = modelManager;
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.syncManager = syncManager;
|
this.syncManager = syncManager;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
this.$compile = $compile;
|
||||||
|
this.$timeout = $timeout;
|
||||||
|
|
||||||
|
// Used when decrypting old items with new keys. This array is only kept in memory.
|
||||||
|
this.previousPasswords = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
get extensions() {
|
get extensions() {
|
||||||
@@ -46,36 +52,83 @@ class ActionsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executeAction(action, extension, item, callback) {
|
async executeAction(action, extension, item, callback) {
|
||||||
|
|
||||||
var customCallback = function(response) {
|
var customCallback = (response) => {
|
||||||
action.running = false;
|
action.running = false;
|
||||||
|
this.$timeout(() => {
|
||||||
callback(response);
|
callback(response);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
action.running = true;
|
action.running = true;
|
||||||
|
|
||||||
let decrypted = action.access_type == "decrypted";
|
let decrypted = action.access_type == "decrypted";
|
||||||
|
|
||||||
switch (action.verb) {
|
var triedPasswords = [];
|
||||||
case "get": {
|
|
||||||
|
|
||||||
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
let handleResponseDecryption = async (response, keys, merge) => {
|
||||||
action.error = false;
|
var item = response.item;
|
||||||
var items = response.items || [response.item];
|
|
||||||
SFJS.itemTransformer.decryptMultipleItems(items, this.authManager.keys()).then(() => {
|
await SFJS.itemTransformer.decryptItem(item, keys);
|
||||||
items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved);
|
|
||||||
for(var item of items) {
|
if(!item.errorDecrypting) {
|
||||||
item.setDirty(true);
|
if(merge) {
|
||||||
|
var items = this.modelManager.mapResponseItemsToLocalModels([item], ModelManager.MappingSourceRemoteActionRetrieved);
|
||||||
|
for(var mappedItem of items) {
|
||||||
|
mappedItem.setDirty(true);
|
||||||
}
|
}
|
||||||
this.syncManager.sync(null);
|
this.syncManager.sync(null);
|
||||||
customCallback({items: items});
|
customCallback({item: item});
|
||||||
})
|
} else {
|
||||||
|
item = this.modelManager.createItem(item, true /* Dont notify observers */);
|
||||||
|
customCallback({item: item});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Error decrypting
|
||||||
|
if(!response.auth_params) {
|
||||||
|
// In some cases revisions were missing auth params. Instruct the user to email us to get this remedied.
|
||||||
|
alert("We were unable to decrypt this revision using your current keys, and this revision is missing metadata that would allow us to try different keys to decrypt it. This can likely be fixed with some manual intervention. Please email hello@standardnotes.org for assistance.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try previous passwords
|
||||||
|
for(let passwordCandidate of this.previousPasswords) {
|
||||||
|
if(triedPasswords.includes(passwordCandidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
triedPasswords.push(passwordCandidate);
|
||||||
|
|
||||||
|
var keyResults = await SFJS.crypto.computeEncryptionKeysForUser(passwordCandidate, response.auth_params);
|
||||||
|
if(!keyResults) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = await handleResponseDecryption(response, keyResults, merge);
|
||||||
|
if(success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.presentPasswordModal((password) => {
|
||||||
|
this.previousPasswords.push(password);
|
||||||
|
handleResponseDecryption(response, keys, merge);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action.verb) {
|
||||||
|
case "get": {
|
||||||
|
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
||||||
|
action.error = false;
|
||||||
|
handleResponseDecryption(response, this.authManager.keys(), true);
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
action.error = true;
|
action.error = true;
|
||||||
customCallback(null);
|
customCallback(null);
|
||||||
})
|
})
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,10 +136,7 @@ class ActionsManager {
|
|||||||
|
|
||||||
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
||||||
action.error = false;
|
action.error = false;
|
||||||
SFJS.itemTransformer.decryptItem(response.item, this.authManager.keys()).then(() => {
|
handleResponseDecryption(response, this.authManager.keys(), false);
|
||||||
var item = this.modelManager.createItem(response.item, true /* Dont notify observers */);
|
|
||||||
customCallback({item: item});
|
|
||||||
})
|
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
action.error = true;
|
action.error = true;
|
||||||
customCallback(null);
|
customCallback(null);
|
||||||
@@ -148,6 +198,17 @@ class ActionsManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presentPasswordModal(callback) {
|
||||||
|
|
||||||
|
var scope = this.$rootScope.$new(true);
|
||||||
|
scope.type = "password";
|
||||||
|
scope.title = "Decryption Assistance";
|
||||||
|
scope.message = "Unable to decrypt this item with your current keys. Please enter your account password at the time of this revision.";
|
||||||
|
scope.callback = callback;
|
||||||
|
var el = this.$compile( "<input-modal type='type' message='message' title='title' callback='callback'></input-modal>" )(scope);
|
||||||
|
angular.element(document.body).append(el);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.module('app').service('actionsManager', ActionsManager);
|
angular.module('app').service('actionsManager', ActionsManager);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
access to this note.
|
access to this note.
|
||||||
|
|
||||||
|
|
||||||
.modal.medium-text{"ng-if" => "renderData.showRenderModal", "ng-click" => "$event.stopPropagation();"}
|
.modal.medium-text.medium{"ng-if" => "renderData.showRenderModal", "ng-click" => "$event.stopPropagation();"}
|
||||||
.content
|
.content
|
||||||
.sn-component
|
.sn-component
|
||||||
.panel
|
.panel
|
||||||
@@ -29,4 +29,4 @@
|
|||||||
%a.close-button.info{"ng-click" => "renderData.showRenderModal = false; $event.stopPropagation();"} Close
|
%a.close-button.info{"ng-click" => "renderData.showRenderModal = false; $event.stopPropagation();"} Close
|
||||||
.content.selectable
|
.content.selectable
|
||||||
%h2 {{renderData.title}}
|
%h2 {{renderData.title}}
|
||||||
%p.normal{"style" => "white-space: pre-wrap; font-family: monospace; font-size: 16px;"} {{renderData.text}}
|
%p.normal{"style" => "white-space: pre-wrap; font-size: 16px;"} {{renderData.text}}
|
||||||
|
|||||||
18
app/assets/templates/directives/input-modal.html.haml
Normal file
18
app/assets/templates/directives/input-modal.html.haml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.modal.small.auto-height
|
||||||
|
.content
|
||||||
|
.sn-component
|
||||||
|
.panel
|
||||||
|
.header
|
||||||
|
%h1.title {{title}}
|
||||||
|
%a.close-button{"ng-click" => "dismiss()"} Close
|
||||||
|
.content
|
||||||
|
.panel-section
|
||||||
|
%p.panel-row {{message}}
|
||||||
|
.panel-row
|
||||||
|
.panel-column.stretch
|
||||||
|
%form{"ng-submit" => "submit()"}
|
||||||
|
%input.form-control{:type => '{{type}}', "ng-model" => "formData.input", "placeholder" => "{{placeholder}}", "sn-autofocus" => "true", "should-focus" => "true"}
|
||||||
|
|
||||||
|
.footer
|
||||||
|
%a.right{"ng-click" => "submit()"}
|
||||||
|
Submit
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
%div{"ng-if" => "step == 2"}
|
%div{"ng-if" => "step == 2"}
|
||||||
%p.panel-row
|
%p.panel-row
|
||||||
As a result of this process, your encryption keys will change.
|
As a result of this process, your encryption keys will change.
|
||||||
Any devices on which you use Standard Notes will need to end their session. After this process completes, you'll be asked to sign back in.
|
Any devices on which you use Standard Notes will need to end their session. After this process completes, you will be asked to sign back in.
|
||||||
|
|
||||||
%p.bold.panel-row.info-i Please sign out of all applications (excluding this one), including:
|
%p.bold.panel-row.info-i Please sign out of all applications (excluding this one), including:
|
||||||
%ul
|
%ul
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
.right
|
.right
|
||||||
|
|
||||||
.item{"ng-if" => "ctrl.securityUpdateAvailable == true", "ng-click" => "ctrl.openSecurityUpdate()"}
|
.item{"ng-if" => "ctrl.securityUpdateAvailable", "ng-click" => "ctrl.openSecurityUpdate()"}
|
||||||
%span.success.label Security update available.
|
%span.success.label Security update available.
|
||||||
|
|
||||||
.item{"ng-if" => "ctrl.newUpdateAvailable == true", "ng-click" => "ctrl.clickedNewUpdateAnnouncement()"}
|
.item{"ng-if" => "ctrl.newUpdateAvailable == true", "ng-click" => "ctrl.clickedNewUpdateAnnouncement()"}
|
||||||
|
|||||||
Reference in New Issue
Block a user