Merge privileges
This commit is contained in:
@@ -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;
|
||||
@@ -386,16 +387,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ angular.module('app')
|
||||
}
|
||||
})
|
||||
.controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager,
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager, nativeExtManager) {
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager, nativeExtManager,
|
||||
privilegesManager) {
|
||||
|
||||
authManager.checkForSecurityUpdate().then((available) => {
|
||||
this.securityUpdateAvailable = available;
|
||||
@@ -172,7 +173,31 @@ angular.module('app')
|
||||
}
|
||||
}
|
||||
|
||||
this.selectRoom = function(room) {
|
||||
room.showRoom = !room.showRoom;
|
||||
this.selectRoom = async function(room) {
|
||||
let run = () => {
|
||||
$timeout(() => {
|
||||
room.showRoom = !room.showRoom;
|
||||
})
|
||||
}
|
||||
|
||||
if(!room.showRoom) {
|
||||
// About to show, check if has privileges
|
||||
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
this.clickOutsideAccountMenu = function() {
|
||||
if(privilegesManager.authenticationInProgress()) {
|
||||
return;
|
||||
}
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
angular.module('app')
|
||||
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager,
|
||||
dbManager, syncManager, authManager, themeManager, passcodeManager, storageManager, migrationManager) {
|
||||
dbManager, syncManager, authManager, themeManager, passcodeManager, storageManager, migrationManager,
|
||||
privilegesManager) {
|
||||
|
||||
storageManager.initialize(passcodeManager.hasPasscode(), authManager.isEphemeralSession());
|
||||
|
||||
@@ -83,14 +84,14 @@ angular.module('app')
|
||||
syncManager.loadLocalItems().then(() => {
|
||||
$timeout(() => {
|
||||
$scope.allTag.didLoad = true;
|
||||
$rootScope.$broadcast("initial-data-loaded");
|
||||
$rootScope.$broadcast("initial-data-loaded"); // This needs to be processed first before sync is called so that singletonManager observers function properly.
|
||||
syncManager.sync();
|
||||
// refresh every 30s
|
||||
setInterval(function () {
|
||||
syncManager.sync();
|
||||
}, 30000);
|
||||
})
|
||||
|
||||
syncManager.sync();
|
||||
// refresh every 30s
|
||||
setInterval(function () {
|
||||
syncManager.sync();
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
authManager.addEventHandler((event) => {
|
||||
|
||||
@@ -41,7 +41,6 @@ class LockScreen {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('lockScreen', () => new LockScreen);
|
||||
|
||||
@@ -31,7 +31,8 @@ angular.module('app')
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager, desktopManager) {
|
||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager,
|
||||
storageManager, desktopManager, privilegesManager) {
|
||||
|
||||
this.panelController = {};
|
||||
|
||||
@@ -198,19 +199,31 @@ angular.module('app')
|
||||
}
|
||||
}
|
||||
|
||||
this.selectNote = function(note, viaClick = false) {
|
||||
this.selectNote = async function(note, viaClick = false) {
|
||||
if(!note) {
|
||||
this.createNewNote();
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedNote = note;
|
||||
note.conflict_of = null; // clear conflict
|
||||
this.selectionMade()(note);
|
||||
this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);
|
||||
let run = () => {
|
||||
$timeout(() => {
|
||||
this.selectedNote = note;
|
||||
note.conflict_of = null; // clear conflict
|
||||
this.selectionMade()(note);
|
||||
this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);
|
||||
|
||||
if(viaClick && this.isFiltering()) {
|
||||
desktopManager.searchText(this.noteFilter.text);
|
||||
if(viaClick && this.isFiltering()) {
|
||||
desktopManager.searchText(this.noteFilter.text);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(note.locked && await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionViewLockedNotes)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionViewLockedNotes, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ angular.module('app').directive('infiniteScroll', [
|
||||
'$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) {
|
||||
return {
|
||||
link: function(scope, elem, attrs) {
|
||||
// elem.css('overflow-x', 'hidden');
|
||||
// elem.css('height', 'inherit');
|
||||
|
||||
var offset = parseInt(attrs.threshold) || 0;
|
||||
var e = elem[0]
|
||||
|
||||
|
||||
15
app/assets/javascripts/app/directives/functional/snEnter.js
Normal file
15
app/assets/javascripts/app/directives/functional/snEnter.js
Normal file
@@ -0,0 +1,15 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('snEnter', function() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind("keydown keypress", function(event) {
|
||||
if(event.which === 13) {
|
||||
scope.$apply(function(){
|
||||
scope.$eval(attrs.snEnter, {'event': event});
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -10,7 +10,7 @@ class AccountMenu {
|
||||
}
|
||||
|
||||
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
||||
$timeout, $compile, archiveManager) {
|
||||
$timeout, $compile, archiveManager, privilegesManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {mergeLocal: true, ephemeral: false};
|
||||
@@ -38,7 +38,6 @@ class AccountMenu {
|
||||
}
|
||||
|
||||
$scope.canAddPasscode = !authManager.isEphemeralSession();
|
||||
|
||||
$scope.syncStatus = syncManager.syncStatus;
|
||||
|
||||
$scope.submitMfaForm = function() {
|
||||
@@ -167,10 +166,27 @@ class AccountMenu {
|
||||
$scope.openPasswordWizard = function(type) {
|
||||
// Close the account menu
|
||||
$scope.close();
|
||||
|
||||
authManager.presentPasswordWizard(type);
|
||||
}
|
||||
|
||||
$scope.openPrivilegesModal = async function() {
|
||||
$scope.close();
|
||||
|
||||
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
|
||||
// clearAllModels will remove data from backing store, but not from working memory
|
||||
// See: https://github.com/standardnotes/desktop/issues/131
|
||||
@@ -229,36 +245,49 @@ class AccountMenu {
|
||||
})
|
||||
}
|
||||
|
||||
$scope.importFileSelected = function(files) {
|
||||
$scope.importData = {};
|
||||
$scope.importFileSelected = async function(files) {
|
||||
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
var data = JSON.parse(e.target.result);
|
||||
$timeout(function(){
|
||||
if(data.auth_params) {
|
||||
// request password
|
||||
$scope.importData.requestPassword = true;
|
||||
$scope.importData.data = data;
|
||||
let run = () => {
|
||||
$timeout(() => {
|
||||
$scope.importData = {};
|
||||
|
||||
$timeout(() => {
|
||||
var element = document.getElementById("import-password-request");
|
||||
if(element) {
|
||||
element.scrollIntoView(false);
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
var data = JSON.parse(e.target.result);
|
||||
$timeout(function(){
|
||||
if(data.auth_params) {
|
||||
// request password
|
||||
$scope.importData.requestPassword = true;
|
||||
$scope.importData.data = data;
|
||||
|
||||
$timeout(() => {
|
||||
var element = document.getElementById("import-password-request");
|
||||
if(element) {
|
||||
element.scrollIntoView(false);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
$scope.performImport(data, null);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
$scope.performImport(data, null);
|
||||
} catch (e) {
|
||||
alert("Unable to open file. Ensure it is a proper JSON file and try again.");
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
alert("Unable to open file. Ensure it is a proper JSON file and try again.");
|
||||
}
|
||||
}
|
||||
|
||||
reader.readAsText(file);
|
||||
})
|
||||
}
|
||||
|
||||
reader.readAsText(file);
|
||||
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.importJSONData = function(data, password, callback) {
|
||||
@@ -316,8 +345,18 @@ class AccountMenu {
|
||||
Export
|
||||
*/
|
||||
|
||||
$scope.downloadDataArchive = function() {
|
||||
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
||||
$scope.downloadDataArchive = async function() {
|
||||
let run = () => {
|
||||
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
||||
}
|
||||
|
||||
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -362,6 +401,35 @@ class AccountMenu {
|
||||
Passcode Lock
|
||||
*/
|
||||
|
||||
$scope.passcodeAutoLockOptions = passcodeManager.getAutoLockIntervalOptions();
|
||||
|
||||
$scope.reloadAutoLockInterval = function() {
|
||||
passcodeManager.getAutoLockInterval().then((interval) => {
|
||||
$timeout(() => {
|
||||
$scope.selectedAutoLockInterval = interval;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
$scope.reloadAutoLockInterval();
|
||||
|
||||
$scope.selectAutoLockInterval = async function(interval) {
|
||||
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() {
|
||||
return passcodeManager.hasPasscode();
|
||||
}
|
||||
@@ -377,10 +445,10 @@ class AccountMenu {
|
||||
return;
|
||||
}
|
||||
|
||||
let fn = $scope.formData.changingPasscode ? passcodeManager.changePasscode : passcodeManager.setPasscode;
|
||||
let fn = $scope.formData.changingPasscode ? passcodeManager.changePasscode.bind(passcodeManager) : passcodeManager.setPasscode.bind(passcodeManager);
|
||||
|
||||
fn(passcode, () => {
|
||||
$timeout(function(){
|
||||
$timeout(() => {
|
||||
$scope.formData.showPasscodeForm = false;
|
||||
var offline = authManager.offline();
|
||||
|
||||
@@ -393,27 +461,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ class MenuRow {
|
||||
this.scope = {
|
||||
action: "&",
|
||||
circle: "=",
|
||||
circleAlign: "=",
|
||||
label: "=",
|
||||
subtitle: "=",
|
||||
hasButton: "=",
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
The purpose of the conflict resoltion modal is to present two versions of a conflicted item,
|
||||
and allow the user to choose which to keep (or to keep both.)
|
||||
*/
|
||||
|
||||
class PrivilegesAuthModal {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/privileges-auth-modal.html";
|
||||
this.scope = {
|
||||
action: "=",
|
||||
onSuccess: "=",
|
||||
onCancel: "=",
|
||||
};
|
||||
}
|
||||
|
||||
link($scope, el, attrs) {
|
||||
$scope.dismiss = function() {
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
controller($scope, privilegesManager, passcodeManager, authManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.authenticationParameters = {};
|
||||
$scope.sessionLengthOptions = privilegesManager.getSessionLengthOptions();
|
||||
|
||||
privilegesManager.getSelectedSessionLength().then((length) => {
|
||||
$timeout(() => {
|
||||
$scope.selectedSessionLength = length;
|
||||
})
|
||||
})
|
||||
|
||||
$scope.selectSessionLength = function(length) {
|
||||
$scope.selectedSessionLength = length;
|
||||
}
|
||||
|
||||
privilegesManager.netCredentialsForAction($scope.action).then((credentials) => {
|
||||
$timeout(() => {
|
||||
$scope.requiredCredentials = credentials.sort();
|
||||
});
|
||||
});
|
||||
|
||||
$scope.promptForCredential = function(credential) {
|
||||
return privilegesManager.displayInfoForCredential(credential).prompt;
|
||||
}
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.dismiss();
|
||||
$scope.onCancel && $scope.onCancel();
|
||||
}
|
||||
|
||||
$scope.isCredentialInFailureState = function(credential) {
|
||||
if(!$scope.failedCredentials) {
|
||||
return false;
|
||||
}
|
||||
return $scope.failedCredentials.find((candidate) => {
|
||||
return candidate == credential;
|
||||
}) != null;
|
||||
}
|
||||
|
||||
$scope.validate = function() {
|
||||
var failed = [];
|
||||
for(var cred of $scope.requiredCredentials) {
|
||||
var value = $scope.authenticationParameters[cred];
|
||||
if(!value || value.length == 0) {
|
||||
failed.push(cred);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.failedCredentials = failed;
|
||||
return failed.length == 0;
|
||||
}
|
||||
|
||||
$scope.submit = function() {
|
||||
if(!$scope.validate()) {
|
||||
return;
|
||||
}
|
||||
privilegesManager.authenticateAction($scope.action, $scope.authenticationParameters).then((result) => {
|
||||
$timeout(() => {
|
||||
if(result.success) {
|
||||
privilegesManager.setSessionLength($scope.selectedSessionLength);
|
||||
$scope.onSuccess();
|
||||
$scope.dismiss();
|
||||
} else {
|
||||
$scope.failedCredentials = result.failedCredentials;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('privilegesAuthModal', () => new PrivilegesAuthModal);
|
||||
@@ -0,0 +1,88 @@
|
||||
class PrivilegesManagementModal {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/privileges-management-modal.html";
|
||||
this.scope = {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
link($scope, el, attrs) {
|
||||
$scope.dismiss = function() {
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
controller($scope, privilegesManager, passcodeManager, authManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.dummy = {};
|
||||
|
||||
$scope.hasPasscode = passcodeManager.hasPasscode();
|
||||
$scope.hasAccount = !authManager.offline();
|
||||
|
||||
$scope.displayInfoForCredential = function(credential) {
|
||||
let info = privilegesManager.displayInfoForCredential(credential);
|
||||
if(credential == PrivilegesManager.CredentialLocalPasscode) {
|
||||
info["availability"] = $scope.hasPasscode;
|
||||
} else if(credential == PrivilegesManager.CredentialAccountPassword) {
|
||||
info["availability"] = $scope.hasAccount;
|
||||
} else {
|
||||
info["availability"] = true;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
$scope.displayInfoForAction = function(action) {
|
||||
return privilegesManager.displayInfoForAction(action).label;
|
||||
}
|
||||
|
||||
$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.availableActions = privilegesManager.getAvailableActions();
|
||||
$scope.availableCredentials = privilegesManager.getAvailableCredentials();
|
||||
let sessionEndDate = await privilegesManager.getSessionExpirey();
|
||||
$scope.sessionExpirey = sessionEndDate.toLocaleString();
|
||||
$scope.sessionExpired = new Date() >= sessionEndDate;
|
||||
|
||||
$scope.credentialDisplayInfo = {};
|
||||
for(let cred of $scope.availableCredentials) {
|
||||
$scope.credentialDisplayInfo[cred] = $scope.displayInfoForCredential(cred);
|
||||
}
|
||||
|
||||
privilegesManager.getPrivileges().then((privs) => {
|
||||
$timeout(() => {
|
||||
$scope.privileges = privs;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
$scope.checkboxValueChanged = function(action, credential) {
|
||||
$scope.privileges.toggleCredentialForAction(action, credential);
|
||||
privilegesManager.savePrivileges();
|
||||
}
|
||||
|
||||
$scope.reloadPrivileges();
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.dismiss();
|
||||
$scope.onCancel && $scope.onCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('privilegesManagementModal', () => new PrivilegesManagementModal);
|
||||
34
app/assets/javascripts/app/models/privileges.js
Normal file
34
app/assets/javascripts/app/models/privileges.js
Normal file
@@ -0,0 +1,34 @@
|
||||
class SNPrivileges extends SFItem {
|
||||
|
||||
setCredentialsForAction(action, credentials) {
|
||||
this.content.desktopPrivileges[action] = credentials;
|
||||
}
|
||||
|
||||
getCredentialsForAction(action) {
|
||||
return this.content.desktopPrivileges[action] || [];
|
||||
}
|
||||
|
||||
toggleCredentialForAction(action, credential) {
|
||||
if(this.isCredentialRequiredForAction(action, credential)) {
|
||||
this.removeCredentialForAction(action, credential);
|
||||
} else {
|
||||
this.addCredentialForAction(action, credential);
|
||||
}
|
||||
}
|
||||
|
||||
removeCredentialForAction(action, credential) {
|
||||
_.pull(this.content.desktopPrivileges[action], credential);
|
||||
}
|
||||
|
||||
addCredentialForAction(action, credential) {
|
||||
var credentials = this.getCredentialsForAction(action);
|
||||
credentials.push(credential);
|
||||
this.setCredentialsForAction(action, credentials);
|
||||
}
|
||||
|
||||
isCredentialRequiredForAction(action, credential) {
|
||||
var credentialsRequired = this.getCredentialsForAction(action);
|
||||
return credentialsRequired.includes(credential);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -92,6 +92,13 @@ class AuthManager extends SFAuthManager {
|
||||
}
|
||||
}
|
||||
|
||||
async verifyAccountPassword(password) {
|
||||
let authParams = await this.getAuthParams();
|
||||
let keys = await SFJS.crypto.computeEncryptionKeysForUser(password, authParams);
|
||||
let success = keys.mk === (await this.keys()).mk;
|
||||
return success;
|
||||
}
|
||||
|
||||
async checkForSecurityUpdate() {
|
||||
if(this.offline()) {
|
||||
return false;
|
||||
@@ -128,6 +135,7 @@ class AuthManager extends SFAuthManager {
|
||||
|
||||
let contentTypePredicate = new SFPredicate("content_type", "=", prefsContentType);
|
||||
this.singletonManager.registerSingleton([contentTypePredicate], (resolvedSingleton) => {
|
||||
// console.log("Loaded existing user prefs", resolvedSingleton.uuid);
|
||||
this.userPreferences = resolvedSingleton;
|
||||
this.userPreferencesDidChange();
|
||||
}, (valueCallback) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ class HttpManager extends SFHttpManager {
|
||||
super($timeout);
|
||||
|
||||
this.setJWTRequestHandler(async () => {
|
||||
return storageManager.getItem("jwt");;
|
||||
return storageManager.getItem("jwt");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ SFModelManager.ContentTypeClassMapping = {
|
||||
"SN|Theme" : SNTheme,
|
||||
"SN|Component" : SNComponent,
|
||||
"SF|Extension" : SNServerExtension,
|
||||
"SF|MFA" : SNMfa
|
||||
"SF|MFA" : SNMfa,
|
||||
"SN|Privileges" : SNPrivileges
|
||||
};
|
||||
|
||||
SFItem.AppDomain = "org.standardnotes.sn";
|
||||
|
||||
@@ -1,100 +1,186 @@
|
||||
angular.module('app')
|
||||
.provider('passcodeManager', function () {
|
||||
class PasscodeManager {
|
||||
|
||||
this.$get = function($rootScope, $timeout, modelManager, dbManager, authManager, storageManager) {
|
||||
return new PasscodeManager($rootScope, $timeout, modelManager, dbManager, authManager, storageManager);
|
||||
}
|
||||
constructor(authManager, storageManager) {
|
||||
document.addEventListener('visibilitychange', (e) => {
|
||||
this.documentVisibilityChanged(document.visibilityState);
|
||||
});
|
||||
|
||||
function PasscodeManager($rootScope, $timeout, modelManager, dbManager, authManager, storageManager) {
|
||||
this.authManager = authManager;
|
||||
this.storageManager = storageManager;
|
||||
|
||||
this._hasPasscode = storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null;
|
||||
this._hasPasscode = this.storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null;
|
||||
this._locked = this._hasPasscode;
|
||||
|
||||
this.isLocked = function() {
|
||||
return this._locked;
|
||||
}
|
||||
const MillisecondsPerSecond = 1000;
|
||||
PasscodeManager.AutoLockIntervalNone = 0;
|
||||
PasscodeManager.AutoLockIntervalOneMinute = 60 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalFiveMinutes = 300 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalOneHour = 3600 * MillisecondsPerSecond;
|
||||
|
||||
this.hasPasscode = function() {
|
||||
return this._hasPasscode;
|
||||
}
|
||||
PasscodeManager.AutoLockIntervalKey = "AutoLockIntervalKey";
|
||||
}
|
||||
|
||||
this.keys = function() {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
this.passcodeAuthParams = function() {
|
||||
var authParams = JSON.parse(storageManager.getItemSync("offlineParams", StorageManager.Fixed));
|
||||
if(authParams && !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.
|
||||
authParams.version = "002";
|
||||
} else {
|
||||
authParams.version = "001";
|
||||
}
|
||||
getAutoLockIntervalOptions() {
|
||||
return [
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalNone,
|
||||
label: "None"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneMinute,
|
||||
label: "1 Min"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalFiveMinutes,
|
||||
label: "5 Min"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneHour,
|
||||
label: "1 Hr"
|
||||
}
|
||||
return authParams;
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.unlock = function(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this._keys = keys;
|
||||
this._authParams = params;
|
||||
this.decryptLocalStorage(keys, params).then(() => {
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
this.setPasscode = (passcode, callback) => {
|
||||
var uuid = SFJS.crypto.generateUUIDSync();
|
||||
|
||||
SFJS.crypto.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
this._authParams = authParams;
|
||||
|
||||
// Encrypting will initially clear localStorage
|
||||
this.encryptLocalStorage(keys, authParams);
|
||||
|
||||
// After it's cleared, it's safe to write to it
|
||||
storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
||||
callback(true);
|
||||
});
|
||||
}
|
||||
|
||||
this.changePasscode = (newPasscode, callback) => {
|
||||
this.setPasscode(newPasscode, callback);
|
||||
}
|
||||
|
||||
this.clearPasscode = function() {
|
||||
storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral
|
||||
storageManager.removeItem("offlineParams", StorageManager.Fixed);
|
||||
this._keys = null;
|
||||
this._hasPasscode = false;
|
||||
}
|
||||
|
||||
this.encryptLocalStorage = function(keys, authParams) {
|
||||
storageManager.setKeys(keys, authParams);
|
||||
// Switch to Ephemeral storage, wiping Fixed storage
|
||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
||||
storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
||||
}
|
||||
|
||||
this.decryptLocalStorage = async function(keys, authParams) {
|
||||
storageManager.setKeys(keys, authParams);
|
||||
return storageManager.decryptStorage();
|
||||
documentVisibilityChanged(visbility) {
|
||||
let visible = document.visibilityState == "visible";
|
||||
if(!visible) {
|
||||
this.beginAutoLockTimer();
|
||||
} else {
|
||||
this.cancelAutoLockTimer();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async beginAutoLockTimer() {
|
||||
var interval = await this.getAutoLockInterval();
|
||||
if(interval == PasscodeManager.AutoLockIntervalNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lockTimeout = setTimeout(() => {
|
||||
this.lockApplication();
|
||||
}, interval);
|
||||
}
|
||||
|
||||
cancelAutoLockTimer() {
|
||||
clearTimeout(this.lockTimeout);
|
||||
}
|
||||
|
||||
lockApplication() {
|
||||
window.location.reload();
|
||||
this.cancelAutoLockTimer();
|
||||
}
|
||||
|
||||
isLocked() {
|
||||
return this._locked;
|
||||
}
|
||||
|
||||
hasPasscode() {
|
||||
return this._hasPasscode;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
async setAutoLockInterval(interval) {
|
||||
return this.storageManager.setItem(PasscodeManager.AutoLockIntervalKey, JSON.stringify(interval), StorageManager.FixedEncrypted);
|
||||
}
|
||||
|
||||
async getAutoLockInterval() {
|
||||
let interval = await this.storageManager.getItem(PasscodeManager.AutoLockIntervalKey, StorageManager.FixedEncrypted);
|
||||
if(interval) {
|
||||
return JSON.parse(interval);
|
||||
} else {
|
||||
return PasscodeManager.AutoLockIntervalNone;
|
||||
}
|
||||
}
|
||||
|
||||
passcodeAuthParams() {
|
||||
var authParams = JSON.parse(this.storageManager.getItemSync("offlineParams", StorageManager.Fixed));
|
||||
if(authParams && !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 their version stored in authParams.
|
||||
authParams.version = "002";
|
||||
} else {
|
||||
authParams.version = "001";
|
||||
}
|
||||
}
|
||||
return authParams;
|
||||
}
|
||||
|
||||
async verifyPasscode(passcode) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
var params = this.passcodeAuthParams();
|
||||
let keys = await SFJS.crypto.computeEncryptionKeysForUser(passcode, params);
|
||||
if(keys.pw !== params.hash) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unlock(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this._keys = keys;
|
||||
this._authParams = params;
|
||||
this.decryptLocalStorage(keys, params).then(() => {
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
setPasscode(passcode, callback) {
|
||||
var uuid = SFJS.crypto.generateUUIDSync();
|
||||
|
||||
SFJS.crypto.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
this._authParams = authParams;
|
||||
|
||||
// Encrypting will initially clear localStorage
|
||||
this.encryptLocalStorage(keys, authParams);
|
||||
|
||||
// After it's cleared, it's safe to write to it
|
||||
this.storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
||||
callback(true);
|
||||
});
|
||||
}
|
||||
|
||||
changePasscode(newPasscode, callback) {
|
||||
this.setPasscode(newPasscode, callback);
|
||||
}
|
||||
|
||||
clearPasscode() {
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral
|
||||
this.storageManager.removeItem("offlineParams", StorageManager.Fixed);
|
||||
this._keys = null;
|
||||
this._hasPasscode = false;
|
||||
}
|
||||
|
||||
encryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
// Switch to Ephemeral storage, wiping Fixed storage
|
||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
||||
}
|
||||
|
||||
async decryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
return this.storageManager.decryptStorage();
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('passcodeManager', PasscodeManager);
|
||||
|
||||
307
app/assets/javascripts/app/services/privilegesManager.js
Normal file
307
app/assets/javascripts/app/services/privilegesManager.js
Normal file
@@ -0,0 +1,307 @@
|
||||
class PrivilegesManager {
|
||||
|
||||
constructor(passcodeManager, authManager, singletonManager, modelManager, storageManager, $rootScope, $compile) {
|
||||
this.passcodeManager = passcodeManager;
|
||||
this.authManager = authManager;
|
||||
this.singletonManager = singletonManager;
|
||||
this.modelManager = modelManager;
|
||||
this.storageManager = storageManager;
|
||||
this.$rootScope = $rootScope;
|
||||
this.$compile = $compile;
|
||||
|
||||
this.loadPrivileges();
|
||||
|
||||
PrivilegesManager.CredentialAccountPassword = "CredentialAccountPassword";
|
||||
PrivilegesManager.CredentialLocalPasscode = "CredentialLocalPasscode";
|
||||
|
||||
PrivilegesManager.ActionManageExtensions = "ActionManageExtensions";
|
||||
PrivilegesManager.ActionManageBackups = "ActionManageBackups";
|
||||
PrivilegesManager.ActionViewLockedNotes = "ActionViewLockedNotes";
|
||||
PrivilegesManager.ActionManagePrivileges = "ActionManagePrivileges";
|
||||
PrivilegesManager.ActionManagePasscode = "ActionManagePasscode";
|
||||
PrivilegesManager.ActionDeleteNote = "ActionDeleteNote";
|
||||
|
||||
PrivilegesManager.SessionExpiresAtKey = "SessionExpiresAtKey";
|
||||
PrivilegesManager.SessionLengthKey = "SessionLengthKey";
|
||||
|
||||
PrivilegesManager.SessionLengthNone = 0;
|
||||
PrivilegesManager.SessionLengthFiveMinutes = 300;
|
||||
PrivilegesManager.SessionLengthOneHour = 3600;
|
||||
PrivilegesManager.SessionLengthOneWeek = 604800;
|
||||
|
||||
this.availableActions = [
|
||||
PrivilegesManager.ActionManagePrivileges,
|
||||
PrivilegesManager.ActionManageExtensions,
|
||||
PrivilegesManager.ActionManageBackups,
|
||||
PrivilegesManager.ActionManagePasscode,
|
||||
PrivilegesManager.ActionViewLockedNotes,
|
||||
PrivilegesManager.ActionDeleteNote
|
||||
]
|
||||
|
||||
this.availableCredentials = [
|
||||
PrivilegesManager.CredentialAccountPassword,
|
||||
PrivilegesManager.CredentialLocalPasscode
|
||||
];
|
||||
|
||||
this.sessionLengths = [
|
||||
PrivilegesManager.SessionLengthNone,
|
||||
PrivilegesManager.SessionLengthFiveMinutes,
|
||||
PrivilegesManager.SessionLengthOneHour,
|
||||
PrivilegesManager.SessionLengthOneWeek,
|
||||
PrivilegesManager.SessionLengthIndefinite
|
||||
]
|
||||
}
|
||||
|
||||
getAvailableActions() {
|
||||
return this.availableActions;
|
||||
}
|
||||
|
||||
getAvailableCredentials() {
|
||||
return this.availableCredentials;
|
||||
}
|
||||
|
||||
presentPrivilegesModal(action, onSuccess, onCancel) {
|
||||
if(this.authenticationInProgress()) {
|
||||
onCancel && onCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
let customSuccess = () => {
|
||||
onSuccess && onSuccess();
|
||||
this.currentAuthenticationElement = null;
|
||||
}
|
||||
|
||||
let customCancel = () => {
|
||||
onCancel && onCancel();
|
||||
this.currentAuthenticationElement = null;
|
||||
}
|
||||
|
||||
var scope = this.$rootScope.$new(true);
|
||||
scope.action = action;
|
||||
scope.onSuccess = customSuccess;
|
||||
scope.onCancel = customCancel;
|
||||
var el = this.$compile( "<privileges-auth-modal action='action' on-success='onSuccess' on-cancel='onCancel' class='sk-modal'></privileges-auth-modal>" )(scope);
|
||||
angular.element(document.body).append(el);
|
||||
|
||||
this.currentAuthenticationElement = el;
|
||||
}
|
||||
|
||||
async netCredentialsForAction(action) {
|
||||
let credentials = (await this.getPrivileges()).getCredentialsForAction(action);
|
||||
let netCredentials = [];
|
||||
|
||||
for(var cred of credentials) {
|
||||
if(cred == PrivilegesManager.CredentialAccountPassword) {
|
||||
if(!this.authManager.offline()) {
|
||||
netCredentials.push(cred);
|
||||
}
|
||||
} else if(cred == PrivilegesManager.CredentialLocalPasscode) {
|
||||
if(this.passcodeManager.hasPasscode()) {
|
||||
netCredentials.push(cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return netCredentials;
|
||||
}
|
||||
|
||||
presentPrivilegesManagementModal() {
|
||||
var scope = this.$rootScope.$new(true);
|
||||
var el = this.$compile( "<privileges-management-modal class='sk-modal'></privileges-management-modal>")(scope);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
authenticationInProgress() {
|
||||
return this.currentAuthenticationElement != null;
|
||||
}
|
||||
|
||||
async loadPrivileges() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let prefsContentType = "SN|Privileges";
|
||||
let contentTypePredicate = new SFPredicate("content_type", "=", prefsContentType);
|
||||
this.singletonManager.registerSingleton([contentTypePredicate], (resolvedSingleton) => {
|
||||
this.privileges = resolvedSingleton;
|
||||
if(!this.privileges.content.desktopPrivileges) {
|
||||
this.privileges.content.desktopPrivileges = {};
|
||||
}
|
||||
console.log("Resolved existing privs", resolvedSingleton.uuid);
|
||||
resolve(resolvedSingleton);
|
||||
}, (valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
var privs = new SNPrivileges({content_type: prefsContentType});
|
||||
this.modelManager.addItem(privs);
|
||||
privs.setDirty(true);
|
||||
this.$rootScope.sync();
|
||||
valueCallback(privs);
|
||||
console.log("Creating new privs", privs.uuid);
|
||||
resolve(privs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getPrivileges() {
|
||||
if(this.privileges) {
|
||||
return this.privileges;
|
||||
} else {
|
||||
return this.loadPrivileges();
|
||||
}
|
||||
}
|
||||
|
||||
displayInfoForCredential(credential) {
|
||||
let metadata = {}
|
||||
|
||||
metadata[PrivilegesManager.CredentialAccountPassword] = {
|
||||
label: "Account Password",
|
||||
prompt: "Please enter your account password."
|
||||
}
|
||||
|
||||
metadata[PrivilegesManager.CredentialLocalPasscode] = {
|
||||
label: "Local Passcode",
|
||||
prompt: "Please enter your local passcode."
|
||||
}
|
||||
|
||||
return metadata[credential];
|
||||
}
|
||||
|
||||
displayInfoForAction(action) {
|
||||
let metadata = {};
|
||||
|
||||
metadata[PrivilegesManager.ActionManageExtensions] = {
|
||||
label: "Manage Extensions"
|
||||
};
|
||||
|
||||
metadata[PrivilegesManager.ActionManageBackups] = {
|
||||
label: "Download/Import Backups"
|
||||
};
|
||||
|
||||
metadata[PrivilegesManager.ActionViewLockedNotes] = {
|
||||
label: "View Locked Notes"
|
||||
};
|
||||
|
||||
metadata[PrivilegesManager.ActionManagePrivileges] = {
|
||||
label: "Manage Privileges"
|
||||
};
|
||||
|
||||
metadata[PrivilegesManager.ActionManagePasscode] = {
|
||||
label: "Manage Passcode"
|
||||
}
|
||||
|
||||
metadata[PrivilegesManager.ActionDeleteNote] = {
|
||||
label: "Delete Notes"
|
||||
}
|
||||
|
||||
return metadata[action];
|
||||
}
|
||||
|
||||
getSessionLengthOptions() {
|
||||
return [
|
||||
{
|
||||
value: PrivilegesManager.SessionLengthNone,
|
||||
label: "Don't Remember"
|
||||
},
|
||||
{
|
||||
value: PrivilegesManager.SessionLengthFiveMinutes,
|
||||
label: "5 Minutes"
|
||||
},
|
||||
{
|
||||
value: PrivilegesManager.SessionLengthOneHour,
|
||||
label: "1 Hour"
|
||||
},
|
||||
{
|
||||
value: PrivilegesManager.SessionLengthOneWeek,
|
||||
label: "1 Week"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async setSessionLength(length) {
|
||||
let addToNow = (seconds) => {
|
||||
let date = new Date();
|
||||
date.setSeconds(date.getSeconds() + seconds);
|
||||
return date;
|
||||
}
|
||||
|
||||
let expiresAt = addToNow(length);
|
||||
|
||||
return Promise.all([
|
||||
this.storageManager.setItem(PrivilegesManager.SessionExpiresAtKey, JSON.stringify(expiresAt), StorageManager.FixedEncrypted),
|
||||
this.storageManager.setItem(PrivilegesManager.SessionLengthKey, JSON.stringify(length), StorageManager.FixedEncrypted),
|
||||
])
|
||||
}
|
||||
|
||||
async clearSession() {
|
||||
return this.setSessionLength(PrivilegesManager.SessionLengthNone);
|
||||
}
|
||||
|
||||
async getSelectedSessionLength() {
|
||||
let length = await this.storageManager.getItem(PrivilegesManager.SessionLengthKey, StorageManager.FixedEncrypted);
|
||||
if(length) {
|
||||
return JSON.parse(length);
|
||||
} else {
|
||||
return PrivilegesManager.SessionLengthNone;
|
||||
}
|
||||
}
|
||||
|
||||
async getSessionExpirey() {
|
||||
let expiresAt = await this.storageManager.getItem(PrivilegesManager.SessionExpiresAtKey, StorageManager.FixedEncrypted);
|
||||
if(expiresAt) {
|
||||
return new Date(JSON.parse(expiresAt));
|
||||
} else {
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
|
||||
async actionRequiresPrivilege(action) {
|
||||
let expiresAt = await this.getSessionExpirey();
|
||||
if(expiresAt > new Date()) {
|
||||
return false;
|
||||
}
|
||||
return (await this.netCredentialsForAction(action)).length > 0;
|
||||
}
|
||||
|
||||
async savePrivileges() {
|
||||
let privs = await this.getPrivileges();
|
||||
privs.setDirty(true);
|
||||
this.$rootScope.sync();
|
||||
}
|
||||
|
||||
async authenticateAction(action, credentialAuthMapping) {
|
||||
var requiredCredentials = (await this.netCredentialsForAction(action));
|
||||
var successfulCredentials = [], failedCredentials = [];
|
||||
|
||||
for(let requiredCredential of requiredCredentials) {
|
||||
var passesAuth = await this._verifyAuthenticationParameters(requiredCredential, credentialAuthMapping[requiredCredential]);
|
||||
if(passesAuth) {
|
||||
successfulCredentials.push(requiredCredential);
|
||||
} else {
|
||||
failedCredentials.push(requiredCredential);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: failedCredentials.length == 0,
|
||||
successfulCredentials: successfulCredentials,
|
||||
failedCredentials: failedCredentials
|
||||
}
|
||||
}
|
||||
|
||||
async _verifyAuthenticationParameters(credential, value) {
|
||||
|
||||
let verifyAccountPassword = async (password) => {
|
||||
return this.authManager.verifyAccountPassword(password);
|
||||
}
|
||||
|
||||
let verifyLocalPasscode = async (passcode) => {
|
||||
return this.passcodeManager.verifyPasscode(passcode);
|
||||
}
|
||||
|
||||
if(credential == PrivilegesManager.CredentialAccountPassword) {
|
||||
return verifyAccountPassword(value);
|
||||
} else if(credential == PrivilegesManager.CredentialLocalPasscode) {
|
||||
return verifyLocalPasscode(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('privilegesManager', PrivilegesManager);
|
||||
@@ -108,7 +108,6 @@ class SingletonManager {
|
||||
var singleton = allExtantItemsMatchingPredicate[0];
|
||||
singletonHandler.singleton = singleton;
|
||||
singletonHandler.resolutionCallback(singleton);
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user