diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 7afab70..4d41bac 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -116,6 +116,7 @@ class AppController extends Controller if (!$this->ParamHandler->isRest()) { $this->set('menu', $this->ACL->getMenu()); $this->set('loggedUser', $this->ACL->getUser()); + $this->set('roleAccess', $this->ACL->getRoleAccess(false, false)); } } else if ($this->ParamHandler->isRest()) { throw new MethodNotAllowedException(__('Invalid user credentials.')); @@ -131,18 +132,20 @@ class AppController extends Controller } $this->ACL->checkAccess(); - $this->set('breadcrumb', $this->Navigation->getBreadcrumb()); - $this->set('ajax', $this->request->is('ajax')); - $this->request->getParam('prefix'); - $this->set('baseurl', Configure::read('App.fullBaseUrl')); - if (!empty($user) && !empty($user->user_settings_by_name['ui.bsTheme']['value'])) { - $this->set('bsTheme', $user->user_settings_by_name['ui.bsTheme']['value']); - } else { - $this->set('bsTheme', Configure::check('ui.bsTheme') ? Configure::read('ui.bsTheme') : 'default'); - } + if (!$this->ParamHandler->isRest()) { + $this->set('breadcrumb', $this->Navigation->getBreadcrumb()); + $this->set('ajax', $this->request->is('ajax')); + $this->request->getParam('prefix'); + $this->set('baseurl', Configure::read('App.fullBaseUrl')); + if (!empty($user) && !empty($user->user_settings_by_name['ui.bsTheme']['value'])) { + $this->set('bsTheme', $user->user_settings_by_name['ui.bsTheme']['value']); + } else { + $this->set('bsTheme', Configure::check('ui.bsTheme') ? Configure::read('ui.bsTheme') : 'default'); + } - if ($this->modelClass == 'Tags.Tags') { - $this->set('metaGroup', !empty($this->isAdmin) ? 'Administration' : 'Cerebrate'); + if ($this->modelClass == 'Tags.Tags') { + $this->set('metaGroup', !empty($this->isAdmin) ? 'Administration' : 'Cerebrate'); + } } } @@ -188,4 +191,9 @@ class AppController extends Controller { return $this->RestResponse->viewData($this->ACL->findMissingFunctionNames()); } + + public function getRoleAccess() + { + return $this->RestResponse->viewData($this->ACL->getRoleAccess(false, false)); + } } diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index cd38752..afa70ff 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -37,6 +37,7 @@ class ACLComponent extends Component '*' => [ 'checkPermission' => ['*'], 'generateUUID' => ['*'], + 'getRoleAccess' => ['*'], 'queryACL' => ['perm_admin'] ], 'Alignments' => [ @@ -45,6 +46,9 @@ class ACLComponent extends Component 'index' => ['*'], 'view' => ['*'] ], + 'AuditLogs' => [ + 'index' => ['perm_admin'] + ], 'AuthKeys' => [ 'add' => ['*'], 'delete' => ['*'], @@ -82,19 +86,27 @@ class ACLComponent extends Component 'add' => ['perm_admin'], 'delete' => ['perm_admin'], 'edit' => ['perm_admin'], + 'filtering' => ['*'], 'index' => ['*'], - 'view' => ['*'] + 'tag' => ['perm_tagger'], + 'untag' => ['perm_tagger'], + 'view' => ['*'], + 'viewTags' => ['*'] ], 'Instance' => [ 'home' => ['*'], 'migrate' => ['perm_admin'], 'migrationIndex' => ['perm_admin'], 'rollback' => ['perm_admin'], + 'saveSetting' => ['perm_admin'], + 'searchAll' => ['*'], + 'settings' => ['perm_admin'], 'status' => ['*'] ], 'LocalTools' => [ 'action' => ['perm_admin'], 'add' => ['perm_admin'], + 'batchAction' => ['perm_admin'], 'broodTools' => ['perm_admin'], 'connectionRequest' => ['perm_admin'], 'connectLocal' => ['perm_admin'], @@ -123,7 +135,10 @@ class ACLComponent extends Component 'edit' => ['perm_admin'], 'filtering' => ['*'], 'index' => ['*'], - 'view' => ['*'] + 'tag' => ['perm_tagger'], + 'untag' => ['perm_tagger'], + 'view' => ['*'], + 'viewTags' => ['*'] ], 'Outbox' => [ 'createEntry' => ['perm_admin'], @@ -162,8 +177,22 @@ class ACLComponent extends Component 'login' => ['*'], 'logout' => ['*'], 'register' => ['*'], + 'settings' => ['*'], 'toggle' => ['perm_org_admin'], 'view' => ['*'] + ], + 'UserSettings' => [ + 'index' => ['*'], + 'view' => ['*'], + 'add' => ['*'], + 'edit' => ['*'], + 'delete' => ['*'], + 'getSettingByName' => ['*'], + 'setSetting' => ['*'], + 'saveSetting' => ['*'], + 'getBookmarks' => ['*'], + 'saveBookmark' => ['*'], + 'deleteBookmark' => ['*'] ] ); @@ -267,9 +296,19 @@ class ACLComponent extends Component return true; } //$this->__checkLoggedActions($user, $controller, $action); + if (isset($this->aclList['*'][$action])) { + if ($this->evaluateAccessLeaf('*', $action)) { + return true; + } + } if (!isset($this->aclList[$controller])) { return $this->__error(404, __('Invalid controller.'), $soft); } + return $this->evaluateAccessLeaf($controller, $action); + } + + private function evaluateAccessLeaf(string $controller, string $action): bool + { if (isset($this->aclList[$controller][$action]) && !empty($this->aclList[$controller][$action])) { if (in_array('*', $this->aclList[$controller][$action])) { return true; @@ -397,13 +436,18 @@ class ACLComponent extends Component return $missing; } + public function getRoleAccess($role = false, $url_mode = true) + { + return $this->__checkRoleAccess($role, $url_mode); + } + public function printRoleAccess($content = false) { $results = []; - $this->Role = TableRegistry::get('Role'); + $this->Role = TableRegistry::get('Roles'); $conditions = []; if (is_numeric($content)) { - $conditions = array('Role.id' => $content); + $conditions = array('id' => $content); } $roles = $this->Role->find('all', array( 'recursive' => -1, @@ -419,44 +463,54 @@ class ACLComponent extends Component return $results; } - private function __checkRoleAccess($role) + private function __formatControllerAction(array $results, string $controller, string $action, $url_mode = true): array { - $result = []; - foreach ($this->__aclList as $controller => $actions) { - $controllerNames = Inflector::variable($controller) == Inflector::underscore($controller) ? array(Inflector::variable($controller)) : array(Inflector::variable($controller), Inflector::underscore($controller)); - foreach ($controllerNames as $controllerName) { - foreach ($actions as $action => $permissions) { - if ($role['perm_site_admin']) { - $result[] = DS . $controllerName . DS . $action; - } elseif (in_array('*', $permissions)) { - $result[] = DS . $controllerName . DS . $action . DS . '*'; - } elseif (isset($permissions['OR'])) { - $access = false; - foreach ($permissions['OR'] as $permission) { - if ($role[$permission]) { - $access = true; - } + if ($url_mode) { + $results[] = DS . $controller . DS . $action . DS . '*'; + } else { + $results[$controller][] = $action; + } + return $results; + } + + private function __checkRoleAccess($role = false, $url_mode = true) + { + $results = []; + if ($role === false) { + $role = $this->getUser()['role']; + } + foreach ($this->aclList as $controller => $actions) { + foreach ($actions as $action => $permissions) { + if ($role['perm_admin']) { + $results = $this->__formatControllerAction($results, $controller, $action, $url_mode); + } elseif (in_array('*', $permissions)) { + $results = $this->__formatControllerAction($results, $controller, $action, $url_mode); + } elseif (isset($permissions['OR'])) { + $access = false; + foreach ($permissions['OR'] as $permission) { + if ($role[$permission]) { + $access = true; } - if ($access) { - $result[] = DS . $controllerName . DS . $action . DS . '*'; - } - } elseif (isset($permissions['AND'])) { - $access = true; - foreach ($permissions['AND'] as $permission) { - if ($role[$permission]) { - $access = false; - } - } - if ($access) { - $result[] = DS . $controllerName . DS . $action . DS . '*'; - } - } elseif (isset($permissions[0]) && $role[$permissions[0]]) { - $result[] = DS . $controllerName . DS . $action . DS . '*'; } + if ($access) { + $results = $this->__formatControllerAction($results, $controller, $action, $url_mode); + } + } elseif (isset($permissions['AND'])) { + $access = true; + foreach ($permissions['AND'] as $permission) { + if ($role[$permission]) { + $access = false; + } + } + if ($access) { + $results = $this->__formatControllerAction($results, $controller, $action, $url_mode); + } + } elseif (isset($permissions[0]) && $role[$permissions[0]]) { + $results = $this->__formatControllerAction($results, $controller, $action, $url_mode); } } } - return $result; + return $results; } public function getMenu() diff --git a/src/Controller/EncryptionKeysController.php b/src/Controller/EncryptionKeysController.php index 78bec89..bafe8ce 100644 --- a/src/Controller/EncryptionKeysController.php +++ b/src/Controller/EncryptionKeysController.php @@ -50,19 +50,30 @@ class EncryptionKeysController extends AppController public function add() { $orgConditions = []; + $individualConditions = []; $currentUser = $this->ACL->getUser(); $params = ['redirect' => $this->referer()]; if (empty($currentUser['role']['perm_admin'])) { - $params['beforeSave'] = function($entity) { + $orgConditions = [ + 'id' => $currentUser['organisation_id'] + ]; + if (empty($currentUser['role']['perm_org_admin'])) { + $individualConditions = [ + 'id' => $currentUser['individual_id'] + ]; + } + $params['beforeSave'] = function($entity) use($currentUser) { if ($entity['owner_model'] === 'organisation') { $entity['owner_id'] = $currentUser['organisation_id']; } else { if ($currentUser['role']['perm_org_admin']) { - $validIndividuals = $this->Organisations->Alignments->find('list', [ - 'fields' => ['distinct(individual_id)'], + $this->loadModel('Alignments'); + $validIndividuals = $this->Alignments->find('list', [ + 'keyField' => 'individual_id', + 'valueField' => 'id', 'conditions' => ['organisation_id' => $currentUser['organisation_id']] - ]); - if (!in_array($entity['owner_id'], $validIndividuals)) { + ])->toArray(); + if (!isset($validIndividuals[$entity['owner_id']])) { throw new MethodNotAllowedException(__('Selected individual cannot be linked by the current user.')); } } else { @@ -71,6 +82,7 @@ class EncryptionKeysController extends AppController } } } + return $entity; }; } $this->CRUD->add($params); @@ -86,7 +98,8 @@ class EncryptionKeysController extends AppController 'conditions' => $orgConditions ]), 'individual' => $this->Individuals->find('list', [ - 'sort' => ['email' => 'asc'] + 'sort' => ['email' => 'asc'], + 'conditions' => $individualConditions ]) ]; $this->set(compact('dropdownData')); diff --git a/src/View/AppView.php b/src/View/AppView.php index 7636a87..9018168 100644 --- a/src/View/AppView.php +++ b/src/View/AppView.php @@ -44,5 +44,6 @@ class AppView extends View $this->loadHelper('FormFieldMassage'); $this->loadHelper('Paginator', ['templates' => 'cerebrate-pagination-templates']); $this->loadHelper('Tags.Tag'); + $this->loadHelper('ACL'); } } diff --git a/src/View/Helper/ACLHelper.php b/src/View/Helper/ACLHelper.php new file mode 100644 index 0000000..e563e82 --- /dev/null +++ b/src/View/Helper/ACLHelper.php @@ -0,0 +1,25 @@ +roleAccess)) { + $this->roleAccess = $this->getView()->get('roleAccess'); + } + if ( + in_array($action, $this->roleAccess['*']) || + (isset($this->roleAccess[$controller]) && in_array($action, $this->roleAccess[$controller])) + ) { + return true; + } else { + return false; + } + } +} diff --git a/templates/SharingGroups/index.php b/templates/SharingGroups/index.php index 2aae744..c930bed 100644 --- a/templates/SharingGroups/index.php +++ b/templates/SharingGroups/index.php @@ -10,7 +10,8 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'data' => [ 'type' => 'simple', 'text' => __('Add sharing group'), - 'popover_url' => '/SharingGroups/add' + 'popover_url' => '/SharingGroups/add', + 'requirement' => $this->ACL->checkAccess('SharingGroups', 'add') ] ] ], @@ -48,9 +49,9 @@ echo $this->element('genericElements/IndexTable/index_table', [ ], [ 'name' => __('Members'), - 'data_path' => 'alignments', + 'data_path' => 'sharing_group_orgs', 'element' => 'count_summary', - 'url' => '/sharingGroups/view/{{id}}', + 'url' => '/sharingGroups/view/{{url_data}}', 'url_data_path' => 'id' ] ], diff --git a/templates/Users/add.php b/templates/Users/add.php index f215277..a3c90a8 100644 --- a/templates/Users/add.php +++ b/templates/Users/add.php @@ -25,7 +25,8 @@ 'label' => __('Password'), 'type' => 'password', 'required' => $this->request->getParam('action') === 'add' ? 'required' : false, - 'autocomplete' => 'new-password' + 'autocomplete' => 'new-password', + 'value' => '' ], [ 'field' => 'confirm_password', diff --git a/templates/element/genericElements/Form/Fields/dropdownField.php b/templates/element/genericElements/Form/Fields/dropdownField.php index 46b4c81..0bfc0e5 100644 --- a/templates/element/genericElements/Form/Fields/dropdownField.php +++ b/templates/element/genericElements/Form/Fields/dropdownField.php @@ -2,7 +2,7 @@ $controlParams = [ 'options' => $fieldData['options'], 'empty' => $fieldData['empty'] ?? false, - 'value' => $fieldData['value'] ?? [], + 'value' => $fieldData['value'] ?? null, 'multiple' => $fieldData['multiple'] ?? false, 'disabled' => $fieldData['disabled'] ?? false, 'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select' diff --git a/templates/element/genericElements/Form/Fields/passwordField.php b/templates/element/genericElements/Form/Fields/passwordField.php new file mode 100644 index 0000000..6831ce6 --- /dev/null +++ b/templates/element/genericElements/Form/Fields/passwordField.php @@ -0,0 +1,6 @@ +FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData); +?>