Privs wip
This commit is contained in:
@@ -23,7 +23,8 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager,
|
.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) => {
|
authManager.checkForSecurityUpdate().then((available) => {
|
||||||
this.securityUpdateAvailable = available;
|
this.securityUpdateAvailable = available;
|
||||||
@@ -173,6 +174,26 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.selectRoom = function(room) {
|
this.selectRoom = function(room) {
|
||||||
room.showRoom = !room.showRoom;
|
let run = () => {
|
||||||
|
room.showRoom = !room.showRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!room.showRoom) {
|
||||||
|
// About to show, check if has privileges
|
||||||
|
if(privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
||||||
|
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clickOutsideAccountMenu = function() {
|
||||||
|
if(privilegesManager.authenticationInProgress()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.showAccountMenu = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
angular.module('app')
|
angular.module('app')
|
||||||
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager,
|
.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());
|
storageManager.initialize(passcodeManager.hasPasscode(), authManager.isEphemeralSession());
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class LockScreen {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.module('app').directive('lockScreen', () => new LockScreen);
|
angular.module('app').directive('lockScreen', () => new LockScreen);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class AccountMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
||||||
$timeout, $compile, archiveManager) {
|
$timeout, $compile, archiveManager, privilegesManager) {
|
||||||
'ngInject';
|
'ngInject';
|
||||||
|
|
||||||
$scope.formData = {mergeLocal: true, ephemeral: false};
|
$scope.formData = {mergeLocal: true, ephemeral: false};
|
||||||
@@ -317,7 +317,17 @@ class AccountMenu {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$scope.downloadDataArchive = function() {
|
$scope.downloadDataArchive = function() {
|
||||||
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
let run = () => {
|
||||||
|
archiveManager.downloadBackup($scope.archiveFormData.encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionDownloadBackup)) {
|
||||||
|
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionDownloadBackup, () => {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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, $timeout) {
|
||||||
|
'ngInject';
|
||||||
|
|
||||||
|
$scope.privileges = privilegesManager.privilegesForAction($scope.action);
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$scope.dismiss();
|
||||||
|
$scope.onCancel && $scope.onCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.doesPrivHaveFail = function(priv) {
|
||||||
|
if(!$scope.failedPrivs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $scope.failedPrivs.find((failedPriv) => {
|
||||||
|
return failedPriv.name == priv.name;
|
||||||
|
}) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.submit = function() {
|
||||||
|
privilegesManager.verifyPrivilegesForAction($scope.action, $scope.privileges).then((result) => {
|
||||||
|
console.log("Result", result);
|
||||||
|
$timeout(() => {
|
||||||
|
if(result.success) {
|
||||||
|
$scope.onSuccess();
|
||||||
|
$scope.dismiss();
|
||||||
|
} else {
|
||||||
|
$scope.failedPrivs = result.failedPrivs;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app').directive('privilegesAuthModal', () => new PrivilegesAuthModal);
|
||||||
@@ -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() {
|
async checkForSecurityUpdate() {
|
||||||
if(this.offline()) {
|
if(this.offline()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -36,6 +36,18 @@ angular.module('app')
|
|||||||
return authParams;
|
return authParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.verifyPasscode = async function(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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.unlock = function(passcode, callback) {
|
this.unlock = function(passcode, callback) {
|
||||||
var params = this.passcodeAuthParams();
|
var params = this.passcodeAuthParams();
|
||||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||||
|
|||||||
107
app/assets/javascripts/app/services/privilegesManager.js
Normal file
107
app/assets/javascripts/app/services/privilegesManager.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
class PrivilegesManager {
|
||||||
|
|
||||||
|
constructor(passcodeManager, authManager, $rootScope, $compile) {
|
||||||
|
this.passcodeManager = passcodeManager;
|
||||||
|
this.authManager = authManager;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
this.$compile = $compile;
|
||||||
|
|
||||||
|
PrivilegesManager.PrivilegeAccountPassword = "PrivilegeAccountPassword";
|
||||||
|
PrivilegesManager.PrivilegeLocalPasscode = "PrivilegeLocalPasscode";
|
||||||
|
|
||||||
|
PrivilegesManager.ActionManageExtensions = "ActionManageExtensions";
|
||||||
|
PrivilegesManager.ActionDownloadBackup = "ActionDownloadBackup";
|
||||||
|
}
|
||||||
|
|
||||||
|
presentPrivilegesModal(action, onSuccess, onCancel) {
|
||||||
|
|
||||||
|
let customSuccess = () => {
|
||||||
|
onSuccess();
|
||||||
|
this.currentAuthenticationElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let customCancel = () => {
|
||||||
|
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='modal'></privileges-auth-modal>" )(scope);
|
||||||
|
angular.element(document.body).append(el);
|
||||||
|
|
||||||
|
this.currentAuthenticationElement = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationInProgress() {
|
||||||
|
return this.currentAuthenticationElement != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
privilegesForAction(action) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: PrivilegesManager.PrivilegeAccountPassword,
|
||||||
|
label: "Account Password",
|
||||||
|
prompt: "Please enter your account password."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: PrivilegesManager.PrivilegeLocalPasscode,
|
||||||
|
label: "Local Passcode",
|
||||||
|
prompt: "Please enter your local passcode."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
actionRequiresPrivilege(action) {
|
||||||
|
return this.privilegesForAction(action).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyPrivilegesForAction(action, inputPrivs) {
|
||||||
|
|
||||||
|
let findInputPriv = (name) => {
|
||||||
|
return inputPrivs.find((priv) => {
|
||||||
|
return priv.name == name;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var requiredPrivileges = this.privilegesForAction(action);
|
||||||
|
var successfulPrivs = [], failedPrivs = [];
|
||||||
|
for(let requiredPriv of requiredPrivileges) {
|
||||||
|
var matchingPriv = findInputPriv(requiredPriv.name);
|
||||||
|
var passesAuth = await this.verifyAuthenticationParameters(matchingPriv);
|
||||||
|
if(passesAuth) {
|
||||||
|
successfulPrivs.push(matchingPriv);
|
||||||
|
} else {
|
||||||
|
failedPrivs.push(matchingPriv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: failedPrivs.length == 0,
|
||||||
|
successfulPrivs: successfulPrivs,
|
||||||
|
failedPrivs: failedPrivs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyAuthenticationParameters(parameters) {
|
||||||
|
|
||||||
|
let verifyAccountPassword = async (password) => {
|
||||||
|
return this.authManager.verifyAccountPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
let verifyLocalPasscode = async (passcode) => {
|
||||||
|
return this.passcodeManager.verifyPasscode(passcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parameters.name == PrivilegesManager.PrivilegeAccountPassword) {
|
||||||
|
return verifyAccountPassword(parameters.authenticationValue);
|
||||||
|
} else if(parameters.name == PrivilegesManager.PrivilegeLocalPasscode) {
|
||||||
|
return verifyLocalPasscode(parameters.authenticationValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app').service('privilegesManager', PrivilegesManager);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.background{"ng-click" => "cancel()"}
|
||||||
|
|
||||||
|
.content#privileges-modal
|
||||||
|
.sn-component
|
||||||
|
.panel
|
||||||
|
.header
|
||||||
|
%h1.title Authentication Required
|
||||||
|
%a.close-button.info{"ng-click" => "cancel()"} Cancel
|
||||||
|
.content
|
||||||
|
.panel-section
|
||||||
|
.panel-row{"ng-repeat" => "priv in privileges"}
|
||||||
|
%p {{priv.prompt}}
|
||||||
|
%input{"type" => "password", "ng-model" => "priv.authenticationValue"}
|
||||||
|
%label.danger{"ng-if" => "doesPrivHaveFail(priv)"} Invalid authentication. Please try again.
|
||||||
|
|
||||||
|
.footer
|
||||||
|
.button.info.big.block.bold{"ng-click" => "submit()"} Submit
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
.sn-component
|
.sn-component
|
||||||
#footer-bar.app-bar.no-edges
|
#footer-bar.app-bar.no-edges
|
||||||
.left
|
.left
|
||||||
.item{"ng-click" => "ctrl.accountMenuPressed()", "click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"}
|
.item{"ng-click" => "ctrl.accountMenuPressed()", "click-outside" => "ctrl.clickOutsideAccountMenu()", "is-open" => "ctrl.showAccountMenu"}
|
||||||
.column
|
.column
|
||||||
.circle.small{"ng-class" => "ctrl.error ? 'danger' : (ctrl.getUser() ? 'info' : 'default')"}
|
.circle.small{"ng-class" => "ctrl.error ? 'danger' : (ctrl.getUser() ? 'info' : 'default')"}
|
||||||
.column
|
.column
|
||||||
|
|||||||
Reference in New Issue
Block a user