Privileges modal
This commit is contained in:
@@ -173,14 +173,17 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectRoom = function(room) {
|
this.selectRoom = async function(room) {
|
||||||
let run = () => {
|
let run = () => {
|
||||||
room.showRoom = !room.showRoom;
|
$timeout(() => {
|
||||||
|
room.showRoom = !room.showRoom;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!room.showRoom) {
|
if(!room.showRoom) {
|
||||||
// About to show, check if has privileges
|
// About to show, check if has privileges
|
||||||
if(privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
|
||||||
|
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
||||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
||||||
run();
|
run();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -167,10 +167,13 @@ class AccountMenu {
|
|||||||
$scope.openPasswordWizard = function(type) {
|
$scope.openPasswordWizard = function(type) {
|
||||||
// Close the account menu
|
// Close the account menu
|
||||||
$scope.close();
|
$scope.close();
|
||||||
|
|
||||||
authManager.presentPasswordWizard(type);
|
authManager.presentPasswordWizard(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.openPrivilegesModal = function() {
|
||||||
|
privilegesManager.presentPrivilegesManagementModal();
|
||||||
|
}
|
||||||
|
|
||||||
// Allows indexeddb unencrypted logs to be deleted
|
// Allows indexeddb unencrypted logs to be deleted
|
||||||
// clearAllModels will remove data from backing store, but not from working memory
|
// clearAllModels will remove data from backing store, but not from working memory
|
||||||
// See: https://github.com/standardnotes/desktop/issues/131
|
// See: https://github.com/standardnotes/desktop/issues/131
|
||||||
@@ -316,12 +319,12 @@ class AccountMenu {
|
|||||||
Export
|
Export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$scope.downloadDataArchive = function() {
|
$scope.downloadDataArchive = async function() {
|
||||||
let run = () => {
|
let run = () => {
|
||||||
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionDownloadBackup)) {
|
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionDownloadBackup)) {
|
||||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionDownloadBackup, () => {
|
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionDownloadBackup, () => {
|
||||||
run();
|
run();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ class PrivilegesAuthModal {
|
|||||||
controller($scope, privilegesManager, $timeout) {
|
controller($scope, privilegesManager, $timeout) {
|
||||||
'ngInject';
|
'ngInject';
|
||||||
|
|
||||||
$scope.privileges = privilegesManager.privilegesForAction($scope.action);
|
privilegesManager.requiredCredentialsForAction($scope.action).then((privs) => {
|
||||||
|
$timeout(() => {
|
||||||
|
$scope.privileges = privs;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
$scope.cancel = function() {
|
$scope.cancel = function() {
|
||||||
$scope.dismiss();
|
$scope.dismiss();
|
||||||
@@ -41,7 +45,7 @@ class PrivilegesAuthModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.submit = function() {
|
$scope.submit = function() {
|
||||||
privilegesManager.verifyPrivilegesForAction($scope.action, $scope.privileges).then((result) => {
|
privilegesManager.authenticateAction($scope.action, $scope.privileges).then((result) => {
|
||||||
console.log("Result", result);
|
console.log("Result", result);
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
if(result.success) {
|
if(result.success) {
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
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, $timeout) {
|
||||||
|
'ngInject';
|
||||||
|
|
||||||
|
$scope.reloadPrivileges = async function() {
|
||||||
|
console.log("Reloading privs");
|
||||||
|
$scope.availableActions = privilegesManager.getAvailableActions();
|
||||||
|
$scope.availableCredentials = privilegesManager.getAvailableCredentials();
|
||||||
|
|
||||||
|
let metadata = {};
|
||||||
|
for(let action of $scope.availableActions) {
|
||||||
|
var requiredCreds = await privilegesManager.requiredCredentialsForAction(action);
|
||||||
|
metadata[action] = {
|
||||||
|
displayInfo: privilegesManager.displayInfoForAction(action),
|
||||||
|
requiredCredentials: requiredCreds
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[action]["credentialValues"] = {};
|
||||||
|
for(var availableCred of $scope.availableCredentials) {
|
||||||
|
metadata[action]["credentialValues"][availableCred] = requiredCreds.includes(availableCred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
$scope.metadata = metadata;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.checkboxValueChanged = function(action) {
|
||||||
|
let credentialValues = $scope.metadata[action]["credentialValues"];
|
||||||
|
let keys = Object.keys(credentialValues).filter((key) => {
|
||||||
|
return credentialValues[key] == true;
|
||||||
|
});
|
||||||
|
privilegesManager.setCredentialsForAction(action, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.reloadPrivileges();
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$scope.dismiss();
|
||||||
|
$scope.onCancel && $scope.onCancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app').directive('privilegesManagementModal', () => new PrivilegesManagementModal);
|
||||||
@@ -1,27 +1,49 @@
|
|||||||
class PrivilegesManager {
|
class PrivilegesManager {
|
||||||
|
|
||||||
constructor(passcodeManager, authManager, $rootScope, $compile) {
|
constructor(passcodeManager, authManager, singletonManager, modelManager, $rootScope, $compile) {
|
||||||
this.passcodeManager = passcodeManager;
|
this.passcodeManager = passcodeManager;
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
|
this.singletonManager = singletonManager;
|
||||||
|
this.modelManager = modelManager;
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.$compile = $compile;
|
this.$compile = $compile;
|
||||||
|
|
||||||
PrivilegesManager.PrivilegeAccountPassword = "PrivilegeAccountPassword";
|
this.loadPrivileges();
|
||||||
PrivilegesManager.PrivilegeLocalPasscode = "PrivilegeLocalPasscode";
|
|
||||||
|
PrivilegesManager.CredentialAccountPassword = "CredentialAccountPassword";
|
||||||
|
PrivilegesManager.CredentialLocalPasscode = "CredentialLocalPasscode";
|
||||||
|
|
||||||
PrivilegesManager.ActionManageExtensions = "ActionManageExtensions";
|
PrivilegesManager.ActionManageExtensions = "ActionManageExtensions";
|
||||||
PrivilegesManager.ActionDownloadBackup = "ActionDownloadBackup";
|
PrivilegesManager.ActionDownloadBackup = "ActionDownloadBackup";
|
||||||
|
|
||||||
|
this.availableActions = [
|
||||||
|
PrivilegesManager.ActionManageExtensions,
|
||||||
|
PrivilegesManager.ActionDownloadBackup
|
||||||
|
]
|
||||||
|
|
||||||
|
this.availableCredentials = [
|
||||||
|
PrivilegesManager.CredentialAccountPassword,
|
||||||
|
PrivilegesManager.CredentialLocalPasscode
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvailableActions() {
|
||||||
|
return this.availableActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvailableCredentials() {
|
||||||
|
return this.availableCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
presentPrivilegesModal(action, onSuccess, onCancel) {
|
presentPrivilegesModal(action, onSuccess, onCancel) {
|
||||||
|
|
||||||
let customSuccess = () => {
|
let customSuccess = () => {
|
||||||
onSuccess();
|
onSuccess && onSuccess();
|
||||||
this.currentAuthenticationElement = null;
|
this.currentAuthenticationElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let customCancel = () => {
|
let customCancel = () => {
|
||||||
onCancel();
|
onCancel && onCancel();
|
||||||
this.currentAuthenticationElement = null;
|
this.currentAuthenticationElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,30 +57,98 @@ class PrivilegesManager {
|
|||||||
this.currentAuthenticationElement = el;
|
this.currentAuthenticationElement = el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presentPrivilegesManagementModal() {
|
||||||
|
var scope = this.$rootScope.$new(true);
|
||||||
|
var el = this.$compile( "<privileges-management-modal class='modal'></privileges-management-modal>")(scope);
|
||||||
|
angular.element(document.body).append(el);
|
||||||
|
}
|
||||||
|
|
||||||
authenticationInProgress() {
|
authenticationInProgress() {
|
||||||
return this.currentAuthenticationElement != null;
|
return this.currentAuthenticationElement != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
privilegesForAction(action) {
|
async loadPrivileges() {
|
||||||
return [
|
return new Promise((resolve, reject) => {
|
||||||
{
|
let prefsContentType = "SN|Privileges";
|
||||||
name: PrivilegesManager.PrivilegeAccountPassword,
|
let contentTypePredicate = new SFPredicate("content_type", "=", prefsContentType);
|
||||||
label: "Account Password",
|
this.singletonManager.registerSingleton([contentTypePredicate], (resolvedSingleton) => {
|
||||||
prompt: "Please enter your account password."
|
this.privileges = resolvedSingleton;
|
||||||
},
|
if(!this.privileges.content.desktopPrivileges) {
|
||||||
{
|
this.privileges.content.desktopPrivileges = [];
|
||||||
name: PrivilegesManager.PrivilegeLocalPasscode,
|
}
|
||||||
label: "Local Passcode",
|
resolve(resolvedSingleton);
|
||||||
prompt: "Please enter your local passcode."
|
}, (valueCallback) => {
|
||||||
}
|
// Safe to create. Create and return object.
|
||||||
]
|
var privs = new SFItem({content_type: prefsContentType});
|
||||||
|
this.modelManager.addItem(privs);
|
||||||
|
privs.setDirty(true);
|
||||||
|
this.$rootScope.sync();
|
||||||
|
valueCallback(privs);
|
||||||
|
resolve(privs);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
actionRequiresPrivilege(action) {
|
async getPrivileges() {
|
||||||
return this.privilegesForAction(action).length > 0;
|
if(this.privileges) {
|
||||||
|
return this.privileges;
|
||||||
|
} else {
|
||||||
|
return this.loadPrivileges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyPrivilegesForAction(action, inputPrivs) {
|
async requiredCredentialsForAction(action) {
|
||||||
|
let privs = await this.getPrivileges();
|
||||||
|
return privs.content.desktopPrivileges[action] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ActionDownloadBackup] = {
|
||||||
|
label: "Download Backups"
|
||||||
|
};
|
||||||
|
|
||||||
|
return metadata[action];
|
||||||
|
}
|
||||||
|
|
||||||
|
async actionRequiresPrivilege(action) {
|
||||||
|
return (await this.requiredCredentialsForAction(action)).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCredentialsForAction(action, credentials) {
|
||||||
|
console.log("Setting credentials for action", action, credentials);
|
||||||
|
let privs = await this.getPrivileges();
|
||||||
|
privs.content.desktopPrivileges[action] = credentials;
|
||||||
|
this.savePrivileges();
|
||||||
|
}
|
||||||
|
|
||||||
|
async savePrivileges() {
|
||||||
|
let privs = await this.getPrivileges();
|
||||||
|
privs.setDirty(true);
|
||||||
|
this.$rootScope.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticateAction(action, inputPrivs) {
|
||||||
|
|
||||||
let findInputPriv = (name) => {
|
let findInputPriv = (name) => {
|
||||||
return inputPrivs.find((priv) => {
|
return inputPrivs.find((priv) => {
|
||||||
@@ -66,11 +156,11 @@ class PrivilegesManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredPrivileges = this.privilegesForAction(action);
|
var requiredPrivileges = await this.requiredCredentialsForAction(action);
|
||||||
var successfulPrivs = [], failedPrivs = [];
|
var successfulPrivs = [], failedPrivs = [];
|
||||||
for(let requiredPriv of requiredPrivileges) {
|
for(let requiredPriv of requiredPrivileges) {
|
||||||
var matchingPriv = findInputPriv(requiredPriv.name);
|
var matchingPriv = findInputPriv(requiredPriv.name);
|
||||||
var passesAuth = await this.verifyAuthenticationParameters(matchingPriv);
|
var passesAuth = await this._verifyAuthenticationParameters(matchingPriv);
|
||||||
if(passesAuth) {
|
if(passesAuth) {
|
||||||
successfulPrivs.push(matchingPriv);
|
successfulPrivs.push(matchingPriv);
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +175,7 @@ class PrivilegesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyAuthenticationParameters(parameters) {
|
async _verifyAuthenticationParameters(parameters) {
|
||||||
|
|
||||||
let verifyAccountPassword = async (password) => {
|
let verifyAccountPassword = async (password) => {
|
||||||
return this.authManager.verifyAccountPassword(password);
|
return this.authManager.verifyAccountPassword(password);
|
||||||
@@ -95,9 +185,9 @@ class PrivilegesManager {
|
|||||||
return this.passcodeManager.verifyPasscode(passcode);
|
return this.passcodeManager.verifyPasscode(passcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(parameters.name == PrivilegesManager.PrivilegeAccountPassword) {
|
if(parameters.name == PrivilegesManager.CredentialAccountPassword) {
|
||||||
return verifyAccountPassword(parameters.authenticationValue);
|
return verifyAccountPassword(parameters.authenticationValue);
|
||||||
} else if(parameters.name == PrivilegesManager.PrivilegeLocalPasscode) {
|
} else if(parameters.name == PrivilegesManager.CredentialLocalPasscode) {
|
||||||
return verifyLocalPasscode(parameters.authenticationValue);
|
return verifyLocalPasscode(parameters.authenticationValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,10 @@
|
|||||||
|
|
||||||
.panel-row
|
.panel-row
|
||||||
|
|
||||||
%a.panel-row.condensed{"ng-click" => "openPasswordWizard('change-pw')"} Change Password
|
%a.panel-row.condensed{"ng-click" => "openPasswordWizard('change-pw')"}
|
||||||
|
Change Password
|
||||||
|
%a.panel-row.condensed{"ng-click" => "openPrivilegesModal('')"}
|
||||||
|
Manage Privileges
|
||||||
%a.panel-row.justify-left.condensed.success{"ng-if" => "securityUpdateAvailable", "ng-click" => "openPasswordWizard('upgrade-security')"}
|
%a.panel-row.justify-left.condensed.success{"ng-if" => "securityUpdateAvailable", "ng-click" => "openPasswordWizard('upgrade-security')"}
|
||||||
.inline.circle.small.success.mr-8
|
.inline.circle.small.success.mr-8
|
||||||
.inline Security Update Available
|
.inline Security Update Available
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
.background{"ng-click" => "cancel()"}
|
||||||
|
|
||||||
|
.content#privileges-modal
|
||||||
|
.sn-component
|
||||||
|
.panel
|
||||||
|
.header
|
||||||
|
%h1.title Manage Privileges
|
||||||
|
%a.close-button.info{"ng-click" => "cancel()"} Cancel
|
||||||
|
.content
|
||||||
|
.panel-section
|
||||||
|
%table
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th
|
||||||
|
%th{"ng-repeat" => "cred in availableCredentials"}
|
||||||
|
{{cred}}
|
||||||
|
%tbody
|
||||||
|
%tr{"ng-repeat" => "action in availableActions"}
|
||||||
|
%td
|
||||||
|
%p {{metadata[action].displayInfo.label}}
|
||||||
|
%th{"ng-repeat" => "cred in availableCredentials"}
|
||||||
|
%input{"type" => "checkbox", "ng-model" => "metadata[action]['credentialValues'][cred]", "ng-change" => "checkboxValueChanged(action)"}
|
||||||
|
|
||||||
|
.footer
|
||||||
|
.button.info.big.block.bold{"ng-click" => "submit()"} Save
|
||||||
Reference in New Issue
Block a user