From c5d50728c09a9c56444b5d6bfb16cfe4fa210c83 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Tue, 20 Nov 2018 13:13:58 -0600 Subject: [PATCH] Session clearing, more actions --- .../javascripts/app/controllers/editor.js | 31 +++++-- .../app/directives/views/accountMenu.js | 92 ++++++++++++++----- .../app/directives/views/menuRow.js | 1 + .../views/privilegesManagementModal.js | 19 +++- .../app/services/passcodeManager.js | 1 - .../app/services/privilegesManager.js | 26 ++++-- app/assets/stylesheets/app/_modals.scss | 4 + .../templates/directives/menu-row.html.haml | 5 +- .../privileges-management-modal.html.haml | 4 + app/assets/templates/editor.html.haml | 2 +- 10 files changed, 144 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/app/controllers/editor.js b/app/assets/javascripts/app/controllers/editor.js index 1edba4c77..56e3d93aa 100644 --- a/app/assets/javascripts/app/controllers/editor.js +++ b/app/assets/javascripts/app/controllers/editor.js @@ -22,7 +22,8 @@ angular.module('app') } } }) - .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager, syncManager, modelManager, themeManager, componentManager, storageManager, sessionHistory) { + .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager, + syncManager, modelManager, themeManager, componentManager, storageManager, sessionHistory, privilegesManager) { this.spellcheck = true; this.componentManager = componentManager; @@ -382,16 +383,28 @@ angular.module('app') } } - this.deleteNote = function() { - if(this.note.locked) { - alert("This note is locked. If you'd like to delete it, unlock it, and try again."); - return; + this.deleteNote = async function() { + let run = () => { + $timeout(() => { + if(this.note.locked) { + alert("This note is locked. If you'd like to delete it, unlock it, and try again."); + return; + } + + let title = this.note.safeTitle().length ? `'${this.note.title}'` : "this note"; + if(confirm(`Are you sure you want to delete ${title}?`)) { + this.remove()(this.note); + this.showMenu = false; + } + }); } - let title = this.note.safeTitle().length ? `'${this.note.title}'` : "this note"; - if(confirm(`Are you sure you want to delete ${title}?`)) { - this.remove()(this.note); - this.showMenu = false; + if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionDeleteNote)) { + privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionDeleteNote, () => { + run(); + }); + } else { + run(); } } diff --git a/app/assets/javascripts/app/directives/views/accountMenu.js b/app/assets/javascripts/app/directives/views/accountMenu.js index d7a9c3414..f4df268a3 100644 --- a/app/assets/javascripts/app/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/directives/views/accountMenu.js @@ -169,8 +169,20 @@ class AccountMenu { authManager.presentPasswordWizard(type); } - $scope.openPrivilegesModal = function() { - privilegesManager.presentPrivilegesManagementModal(); + $scope.openPrivilegesModal = async function() { + let run = () => { + $timeout(() => { + privilegesManager.presentPrivilegesManagementModal(); + }) + } + + if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManagePrivileges)) { + privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManagePrivileges, () => { + run(); + }); + } else { + run(); + } } // Allows indexeddb unencrypted logs to be deleted @@ -387,8 +399,20 @@ class AccountMenu { $scope.reloadAutoLockInterval(); $scope.selectAutoLockInterval = async function(interval) { - await passcodeManager.setAutoLockInterval(interval); - $scope.reloadAutoLockInterval(); + let run = async () => { + await passcodeManager.setAutoLockInterval(interval); + $timeout(() => { + $scope.reloadAutoLockInterval(); + }); + } + + if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManagePasscode)) { + privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManagePasscode, () => { + run(); + }); + } else { + run(); + } } $scope.hasPasscode = function() { @@ -422,27 +446,51 @@ class AccountMenu { }) } - $scope.changePasscodePressed = function() { - $scope.formData.changingPasscode = true; - $scope.addPasscodeClicked(); - $scope.formData.changingPasscode = false; + $scope.changePasscodePressed = async function() { + let run = () => { + $timeout(() => { + $scope.formData.changingPasscode = true; + $scope.addPasscodeClicked(); + $scope.formData.changingPasscode = false; + }) + } + + if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManagePasscode)) { + privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManagePasscode, () => { + run(); + }); + } else { + run(); + } } - $scope.removePasscodePressed = function() { - var signedIn = !authManager.offline(); - var message = "Are you sure you want to remove your local passcode?"; - if(!signedIn) { - message += " This will remove encryption from your local data."; - } - if(confirm(message)) { - passcodeManager.clearPasscode(); + $scope.removePasscodePressed = async function() { + let run = () => { + $timeout(() => { + var signedIn = !authManager.offline(); + var message = "Are you sure you want to remove your local passcode?"; + if(!signedIn) { + message += " This will remove encryption from your local data."; + } + if(confirm(message)) { + passcodeManager.clearPasscode(); - if(authManager.offline()) { - syncManager.markAllItemsDirtyAndSaveOffline(); - // Don't create backup here, as if the user is temporarily removing the passcode to change it, - // we don't want to write unencrypted data to disk. - // $rootScope.$broadcast("major-data-change"); - } + if(authManager.offline()) { + syncManager.markAllItemsDirtyAndSaveOffline(); + // Don't create backup here, as if the user is temporarily removing the passcode to change it, + // we don't want to write unencrypted data to disk. + // $rootScope.$broadcast("major-data-change"); + } + } + }) + } + + if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManagePasscode)) { + privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManagePasscode, () => { + run(); + }); + } else { + run(); } } diff --git a/app/assets/javascripts/app/directives/views/menuRow.js b/app/assets/javascripts/app/directives/views/menuRow.js index 999e64f9f..49fbb5e0a 100644 --- a/app/assets/javascripts/app/directives/views/menuRow.js +++ b/app/assets/javascripts/app/directives/views/menuRow.js @@ -7,6 +7,7 @@ class MenuRow { this.scope = { action: "&", circle: "=", + circleAlign: "=", label: "=", subtitle: "=", hasButton: "=", diff --git a/app/assets/javascripts/app/directives/views/privilegesManagementModal.js b/app/assets/javascripts/app/directives/views/privilegesManagementModal.js index b792d3858..d646cb48c 100644 --- a/app/assets/javascripts/app/directives/views/privilegesManagementModal.js +++ b/app/assets/javascripts/app/directives/views/privilegesManagementModal.js @@ -28,13 +28,30 @@ class PrivilegesManagementModal { } $scope.isCredentialRequiredForAction = function(action, credential) { + if(!$scope.privileges) { + return false; + } return $scope.privileges.isCredentialRequiredForAction(action, credential); } + $scope.clearSession = function() { + privilegesManager.clearSession().then(() => { + $scope.reloadPrivileges(); + }) + } + $scope.reloadPrivileges = async function() { - $scope.privileges = await privilegesManager.getPrivileges(); $scope.availableActions = privilegesManager.getAvailableActions(); $scope.availableCredentials = privilegesManager.getAvailableCredentials(); + let sessionEndDate = await privilegesManager.getSessionExpirey(); + $scope.sessionExpirey = sessionEndDate.toLocaleString(); + $scope.sessionExpired = new Date() >= sessionEndDate; + + privilegesManager.getPrivileges().then((privs) => { + $timeout(() => { + $scope.privileges = privs; + }) + }) } $scope.checkboxValueChanged = function(action, credential) { diff --git a/app/assets/javascripts/app/services/passcodeManager.js b/app/assets/javascripts/app/services/passcodeManager.js index 188a1aee7..1aa1f7c5e 100644 --- a/app/assets/javascripts/app/services/passcodeManager.js +++ b/app/assets/javascripts/app/services/passcodeManager.js @@ -2,7 +2,6 @@ class PasscodeManager { constructor(authManager, storageManager) { document.addEventListener('visibilitychange', (e) => { - console.log("visibilitychange", e, document.visibilityState); this.documentVisibilityChanged(document.visibilityState); }); diff --git a/app/assets/javascripts/app/services/privilegesManager.js b/app/assets/javascripts/app/services/privilegesManager.js index 460b23fa3..daed765ab 100644 --- a/app/assets/javascripts/app/services/privilegesManager.js +++ b/app/assets/javascripts/app/services/privilegesManager.js @@ -18,12 +18,14 @@ class PrivilegesManager { PrivilegesManager.ActionDownloadBackup = "ActionDownloadBackup"; PrivilegesManager.ActionViewLockedNotes = "ActionViewLockedNotes"; PrivilegesManager.ActionManagePrivileges = "ActionManagePrivileges"; + PrivilegesManager.ActionManagePasscode = "ActionManagePasscode"; + PrivilegesManager.ActionDeleteNote = "ActionDeleteNote"; PrivilegesManager.SessionExpiresAtKey = "SessionExpiresAtKey"; PrivilegesManager.SessionLengthKey = "SessionLengthKey"; PrivilegesManager.SessionLengthNone = 0; - PrivilegesManager.SessionLengthFiveMinutes = 5; + PrivilegesManager.SessionLengthFiveMinutes = 300; PrivilegesManager.SessionLengthOneHour = 3600; PrivilegesManager.SessionLengthOneWeek = 604800; @@ -31,7 +33,9 @@ class PrivilegesManager { PrivilegesManager.ActionManageExtensions, PrivilegesManager.ActionDownloadBackup, PrivilegesManager.ActionViewLockedNotes, - PrivilegesManager.ActionManagePrivileges + PrivilegesManager.ActionManagePrivileges, + PrivilegesManager.ActionManagePasscode, + PrivilegesManager.ActionDeleteNote ] this.availableCredentials = [ @@ -86,8 +90,6 @@ class PrivilegesManager { if(cred == PrivilegesManager.CredentialAccountPassword) { if(!this.authManager.offline()) { netCredentials.push(cred); - } else { - console.log("WE ARE OFFLINE"); } } else if(cred == PrivilegesManager.CredentialLocalPasscode) { if(this.passcodeManager.hasPasscode()) { @@ -174,6 +176,14 @@ class PrivilegesManager { label: "Manage Privileges" }; + metadata[PrivilegesManager.ActionManagePasscode] = { + label: "Manage Passcode" + } + + metadata[PrivilegesManager.ActionDeleteNote] = { + label: "Delete Note" + } + return metadata[action]; } @@ -185,11 +195,11 @@ class PrivilegesManager { }, { value: PrivilegesManager.SessionLengthFiveMinutes, - label: "5 Min" + label: "5 Minutes" }, { value: PrivilegesManager.SessionLengthOneHour, - label: "1 Hr" + label: "1 Hour" }, { value: PrivilegesManager.SessionLengthOneWeek, @@ -213,6 +223,10 @@ class PrivilegesManager { ]) } + async clearSession() { + return this.setSessionLength(PrivilegesManager.SessionLengthNone); + } + async getSelectedSessionLength() { let length = await this.storageManager.getItem(PrivilegesManager.SessionLengthKey, StorageManager.FixedEncrypted); if(length) { diff --git a/app/assets/stylesheets/app/_modals.scss b/app/assets/stylesheets/app/_modals.scss index 6d6278cbd..074bb4db9 100644 --- a/app/assets/stylesheets/app/_modals.scss +++ b/app/assets/stylesheets/app/_modals.scss @@ -14,6 +14,10 @@ #privileges-modal { width: 700px; + + th { + text-align: left; + } } #password-wizard { diff --git a/app/assets/templates/directives/menu-row.html.haml b/app/assets/templates/directives/menu-row.html.haml index 0cb3cf604..9cdaff83e 100644 --- a/app/assets/templates/directives/menu-row.html.haml +++ b/app/assets/templates/directives/menu-row.html.haml @@ -1,7 +1,7 @@ .row{"ng-attr-title" => "{{desc}}", "ng-click" => "onClick($event)"} .column .left - .column{"ng-if" => "circle"} + .column{"ng-if" => "circle && (!circleAlign || circleAlign == 'left')"} .circle.small{"ng-class" => "circle"} .column{"ng-class" => "{'faded' : faded || disabled}"} .label @@ -13,6 +13,9 @@ %menu-row{"ng-repeat" => "row in subRows", "action" => "row.onClick()", "label" => "row.label", "subtitle" => "row.subtitle", "spinner-class" => "row.spinnerClass"} + .column{"ng-if" => "circle && circleAlign == 'right'"} + .circle.small{"ng-class" => "circle"} + .column{"ng-if" => "hasButton"} .button.info{"ng-click" => "clickButton($event)", "ng-class" => "buttonClass"} {{buttonText}} diff --git a/app/assets/templates/directives/privileges-management-modal.html.haml b/app/assets/templates/directives/privileges-management-modal.html.haml index 52898a899..1a7ce94d1 100644 --- a/app/assets/templates/directives/privileges-management-modal.html.haml +++ b/app/assets/templates/directives/privileges-management-modal.html.haml @@ -20,6 +20,10 @@ %p {{displayInfoForAction(action)}} %th{"ng-repeat" => "credential in availableCredentials"} %input{"type" => "checkbox", "ng-checked" => "isCredentialRequiredForAction(action, credential)", "ng-click" => "checkboxValueChanged(action, credential)"} + + .panel-section{"ng-if" => "sessionExpirey && !sessionExpired"} + %p You will not be asked to authenticate until {{sessionExpirey}}. + %a{"ng-click" => "clearSession()"} Clear Session .footer %h2 About Privileges .panel-section.no-bottom-pad diff --git a/app/assets/templates/editor.html.haml b/app/assets/templates/editor.html.haml index d78f5eca9..332fe91f8 100644 --- a/app/assets/templates/editor.html.haml +++ b/app/assets/templates/editor.html.haml @@ -34,7 +34,7 @@ %menu-row{"label" => "ctrl.note.pinned ? 'Unpin' : 'Pin'", "action" => "ctrl.selectedMenuItem(true); ctrl.togglePin()", "desc" => "'Pin or unpin a note from the top of your list'"} %menu-row{"label" => "ctrl.note.archived ? 'Unarchive' : 'Archive'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleArchiveNote()", "desc" => "'Archive or unarchive a note from your Archived system tag'"} %menu-row{"label" => "ctrl.note.locked ? 'Unlock' : 'Lock'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleLockNote()", "desc" => "'Locking notes prevents unintentional editing'"} - %menu-row{"label" => "ctrl.note.content.hidePreview ? 'Unhide Preview' : 'Hide Preview'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleNotePreview()", "desc" => "'Hide or unhide the note preview from the list of notes'"} + %menu-row{"label" => "'Preview'", "circle" => "ctrl.note.content.hidePreview ? 'danger' : 'success'", "circle-align" => "'right'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleNotePreview()", "desc" => "'Hide or unhide the note preview from the list of notes'"} %menu-row{"label" => "'Delete'", "action" => "ctrl.selectedMenuItem(); ctrl.deleteNote()", "desc" => "'Delete this note permanently from all your devices'"} .section