diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index e740edd..07ec3aa 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -108,7 +108,7 @@ class AppController extends Controller throw new MethodNotAllowedException(__('Invalid user credentials.')); } $this->ACL->checkAccess(); - $this->set('menu', $this->{$this->modelClass}->getMenu()); + $this->set('menu', $this->ACL->getMenu()); $this->set('ajax', $this->request->is('ajax')); $this->request->getParam('prefix'); $this->set('darkMode', !empty(Configure::read('Cerebrate.dark'))); @@ -135,13 +135,8 @@ class AppController extends Controller return $this->RestResponse->viewData(['uuid' => $uuid], 'json'); } - public function checkPermission($perm_flag) - { - return true; - } - public function queryACL() { - $this->ACL->findMissingFunctionNames(); + return $this->RestResponse->viewData($this->ACL->findMissingFunctionNames()); } } diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index f2782ef..0e47a28 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -10,6 +10,7 @@ use Cake\Http\Exception\ForbiddenException; use Cake\ORM\TableRegistry; use Cake\Core\Configure; use Cake\Core\Configure\Engine\PhpConfig; +use Cake\Utility\Inflector; class ACLComponent extends Component { @@ -31,6 +32,22 @@ class ACLComponent extends Component // $action == array('AND' => []) - roles with all permissions in the array have access // If we add any new functionality to MISP and we don't add it to this list, it will only be visible to site admins. private $aclList = array( + '*' => [ + 'checkPermission' => ['*'], + 'generateUUID' => ['*'], + 'queryACL' => ['perm_admin'] + ], + 'Alignments' => [ + 'add' => ['perm_admin'], + 'delete' => ['perm_admin'], + 'index' => ['*'], + 'view' => ['*'] + ], + 'AuthKeys' => [ + 'add' => ['*'], + 'delete' => ['*'], + 'index' => ['*'] + ], 'Broods' => [ 'add' => ['perm_admin'], 'delete' => ['perm_admin'], @@ -38,19 +55,10 @@ class ACLComponent extends Component 'index' => ['perm_admin'], 'view' => ['perm_admin'] ], - 'Instance' => [ - 'home' => ['*'], - 'status' => ['*'] - ], - 'Pages' => [ - 'display' => ['*'] - ], - 'Organisations' => [ - 'add' => ['perm_admin'], - 'delete' => ['perm_admin'], - 'edit' => ['perm_admin'], - 'index' => ['*'], - 'view' => ['*'] + 'EncryptionKeys' => [ + 'add' => ['*'], + 'delete' => ['*'], + 'index' => ['*'] ], 'Individuals' => [ 'add' => ['perm_admin'], @@ -59,13 +67,47 @@ class ACLComponent extends Component 'index' => ['*'], 'view' => ['*'] ], - 'SharingGroups' => [ + 'Instance' => [ + 'home' => ['*'], + 'status' => ['*'] + ], + 'MetaTemplateFields' => [ + 'index' => ['perm_admin'] + ], + 'MetaTemplates' => [ + 'disable' => ['perm_admin'], + 'enable' => ['perm_admin'], + 'index' => ['perm_admin'], + 'update' => ['perm_admin'], + 'view' => ['perm_admin'] + ], + 'Organisations' => [ 'add' => ['perm_admin'], 'delete' => ['perm_admin'], 'edit' => ['perm_admin'], 'index' => ['*'], 'view' => ['*'] ], + 'Pages' => [ + 'display' => ['*'] + ], + 'Roles' => [ + 'add' => ['perm_admin'], + 'delete' => ['perm_admin'], + 'edit' => ['perm_admin'], + 'index' => ['*'], + 'view' => ['*'] + ], + 'SharingGroups' => [ + 'add' => ['perm_admin'], + 'addOrg' => ['perm_admin'], + 'delete' => ['perm_admin'], + 'edit' => ['perm_admin'], + 'index' => ['*'], + 'listOrgs' => ['*'], + 'removeOrg' => ['perm_admin'], + 'view' => ['*'] + ], 'Users' => [ 'add' => ['perm_admin'], 'delete' => ['perm_admin'], @@ -74,13 +116,7 @@ class ACLComponent extends Component 'login' => ['*'], 'logout' => ['*'], 'view' => ['*'] - ], - 'MetaTemplates' => [ - 'view' => ['perm_admin'], - 'enable' => ['perm_admin'], - 'disable' => ['perm_admin'], - 'update' => ['perm_admin'], - ] + ] ); private function __checkLoggedActions($user, $controller, $action) @@ -173,16 +209,8 @@ class ACLComponent extends Component $this->Authentication->allowUnauthenticated(['login']); } - // The check works like this: - // If the user is a site admin, return true - // If the requested action has an OR-d list, iterate through the list. If any of the permissions are set for the user, return true - // If the requested action has an AND-ed list, iterate through the list. If any of the permissions for the user are not set, turn the check to false. Otherwise return true. - // If the requested action has a permission, check if the user's role has it flagged. If yes, return true - // If we fall through all of the checks, return an exception. - public function checkAccess(bool $soft = false): bool + private function checkAccessInternal($controller, $action, $soft): bool { - $controller = $this->request->getParam('controller'); - $action = $this->request->getParam('action'); if (empty($this->user)) { // we have to be in a publically allowed scope otherwise the Auth component will kick us out anyway. return true; @@ -216,13 +244,41 @@ class ACLComponent extends Component } } } + return false; + } + + public function checkAccessUrl($url, $soft = false): bool + { + $urlParts = explode('/', $url); + if ($urlParts[1] === 'open') { + return in_array($urlParts[2], Configure::read('Cerebrate.open')); + } else { + return $this->checkAccessInternal(Inflector::camelize($urlParts[1]), $urlParts[2], $soft); + } + + + } + + // The check works like this: + // If the user is a site admin, return true + // If the requested action has an OR-d list, iterate through the list. If any of the permissions are set for the user, return true + // If the requested action has an AND-ed list, iterate through the list. If any of the permissions for the user are not set, turn the check to false. Otherwise return true. + // If the requested action has a permission, check if the user's role has it flagged. If yes, return true + // If we fall through all of the checks, return an exception. + public function checkAccess(bool $soft = false): bool + { + $controller = $this->request->getParam('controller'); + $action = $this->request->getParam('action'); + if ($this->checkAccessInternal($controller, $action, $soft) === true) { + return true; + } return $this->__error(403, 'You do not have permission to use this functionality.', $soft); } private function __error($code, $message, $soft = false) { if ($soft) { - return $code; + return false; } switch ($code) { case 404: @@ -237,9 +293,13 @@ class ACLComponent extends Component private function __findAllFunctions() { - $functionFinder = '/function[\s\n]+(\S+)[\s\n]*\(/'; - $dir = new Folder(APP . 'Controller'); - $files = $dir->find('.*\.php'); + $functionFinder = '/public.function[\s\n]+(\S+)[\s\n]*\(/'; + $files = scandir(ROOT . '/src/Controller/'); + foreach ($files as $k => $file) { + if (substr($file, -14) !== 'Controller.php') { + unset($files[$k]); + } + } $results = []; foreach ($files as $file) { $controllerName = lcfirst(str_replace('Controller.php', "", $file)); @@ -271,9 +331,13 @@ class ACLComponent extends Component $results = $this->__findAllFunctions(); $missing = []; foreach ($results as $controller => $functions) { + $controller = Inflector::camelize($controller); foreach ($functions as $function) { - if (!isset($this->__aclList[$controller]) - || !in_array($function, array_keys($this->__aclList[$controller]))) { + if (in_array($function, ['beforeFilter', 'beforeRender', 'initialize', 'afterFilter'])) { + continue; + } + if (!isset($this->aclList[$controller]) + || !in_array($function, array_keys($this->aclList[$controller]))) { $missing[$controller][] = $function; } } @@ -342,4 +406,283 @@ class ACLComponent extends Component } return $result; } + + public function getMenu() + { + $open = Configure::read('Cerebrate.open'); + $menu = [ + 'ContactDB' => [ + 'Individuals' => [ + 'label' => __('Individuals'), + 'url' => '/individuals/index', + 'children' => [ + 'index' => [ + 'url' => '/individuals/index', + 'label' => __('List individuals') + ], + 'add' => [ + 'url' => '/individuals/add', + 'label' => __('Add individual') + ], + 'view' => [ + 'url' => '/individuals/view/{{id}}', + 'label' => __('View individual'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'edit' => [ + 'url' => '/individuals/edit/{{id}}', + 'label' => __('Edit individual'), + 'actions' => ['edit', 'delete', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/individuals/delete/{{id}}', + 'label' => __('Delete individual'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ], + 'Organisations' => [ + 'label' => __('Organisations'), + 'url' => '/organisations/index', + 'children' => [ + 'index' => [ + 'url' => '/organisations/index', + 'label' => __('List organisations') + ], + 'add' => [ + 'url' => '/organisations/add', + 'label' => __('Add organisation') + ], + 'view' => [ + 'url' => '/organisations/view/{{id}}', + 'label' => __('View organisation'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'edit' => [ + 'url' => '/organisations/edit/{{id}}', + 'label' => __('Edit organisation'), + 'actions' => ['edit', 'delete', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/organisations/delete/{{id}}', + 'label' => __('Delete organisation'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ], + 'EncryptionKeys' => [ + 'label' => __('Encryption keys'), + 'url' => '/encryptionKeys/index', + 'children' => [ + 'index' => [ + 'url' => '/encryptionKeys/index', + 'label' => __('List encryption keys') + ], + 'add' => [ + 'url' => '/encryptionKeys/add', + 'label' => __('Add encryption key') + ], + 'edit' => [ + 'url' => '/encryptionKeys/edit/{{id}}', + 'label' => __('Edit organisation'), + 'actions' => ['edit'], + 'skipTopMenu' => 1 + ] + ] + ] + ], + 'Trust Circles' => [ + 'SharingGroups' => [ + 'label' => __('Sharing Groups'), + 'url' => '/sharingGroups/index', + 'children' => [ + 'index' => [ + 'url' => '/sharingGroups/index', + 'label' => __('List sharing groups') + ], + 'add' => [ + 'url' => '/SharingGroups/add', + 'label' => __('Add sharing group') + ], + 'edit' => [ + 'url' => '/SharingGroups/edit/{{id}}', + 'label' => __('Edit sharing group'), + 'actions' => ['edit'], + 'skipTopMenu' => 1 + ] + ] + ] + ], + 'Administration' => [ + 'Roles' => [ + 'label' => __('Roles'), + 'url' => '/roles/index', + 'children' => [ + 'index' => [ + 'url' => '/roles/index', + 'label' => __('List roles') + ], + 'add' => [ + 'url' => '/roles/add', + 'label' => __('Add role') + ], + 'view' => [ + 'url' => '/roles/view/{{id}}', + 'label' => __('View role'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'edit' => [ + 'url' => '/roles/edit/{{id}}', + 'label' => __('Edit role'), + 'actions' => ['edit', 'delete', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/roles/delete/{{id}}', + 'label' => __('Delete role'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ], + 'Users' => [ + 'label' => __('Users'), + 'url' => '/users/index', + 'children' => [ + 'index' => [ + 'url' => '/users/index', + 'label' => __('List users') + ], + 'add' => [ + 'url' => '/users/add', + 'label' => __('Add user') + ], + 'view' => [ + 'url' => '/users/view/{{id}}', + 'label' => __('View user'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'edit' => [ + 'url' => '/users/edit/{{id}}', + 'label' => __('Edit user'), + 'actions' => ['edit', 'delete', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/users/delete/{{id}}', + 'label' => __('Delete user'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ], + 'MetaTemplates' => [ + 'label' => __('Meta Field Templates'), + 'url' => '/metaTemplates/index', + 'children' => [ + 'index' => [ + 'url' => '/metaTemplates/index', + 'label' => __('List Meta Templates') + ], + 'view' => [ + 'url' => '/metaTemplates/view/{{id}}', + 'label' => __('View Meta Template'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/metaTemplates/delete/{{id}}', + 'label' => __('Delete Meta Template'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ] + ], + 'Cerebrate' => [ + 'Roles' => [ + 'label' => __('Roles'), + 'url' => '/roles/index', + 'children' => [ + 'index' => [ + 'url' => '/roles/index', + 'label' => __('List roles') + ], + 'view' => [ + 'url' => '/roles/view/{{id}}', + 'label' => __('View role'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ] + ] + ], + 'Instance' => [ + __('Instance'), + 'url' => '/instance/home', + 'children' => [ + 'home' => [ + 'url' => '/instance/home', + 'label' => __('Home') + ] + ] + ] + ], + 'Open' => [ + 'Organisations' => [ + 'label' => __('Organisations'), + 'url' => '/open/organisations/index', + 'children' => [ + 'index' => [ + 'url' => '/open/organisations/index', + 'label' => __('List organisations') + ], + ], + 'open' => in_array('organisations', Configure::read('Cerebrate.open')) + ], + 'Individuals' => [ + 'label' => __('Individuals'), + 'url' => '/open/individuals/index', + 'children' => [ + 'index' => [ + 'url' => '/open/individuals/index', + 'label' => __('List individuals') + ], + ], + 'open' => in_array('individuals', Configure::read('Cerebrate.open')) + ] + ] + ]; + foreach ($menu as $group => $subMenu) { + foreach ($subMenu as $subMenuElementName => $subMenuElement) { + if (!empty($subMenuElement['url']) && !$this->checkAccessUrl($subMenuElement['url'], true) === true) { + unset($menu[$group][$subMenuElementName]); + continue; + } + if (!empty($subMenuElement['children'])) { + foreach ($subMenuElement['children'] as $menuItem => $menuItemData) { + if (!empty($menuItemData['url']) && !$this->checkAccessUrl($menuItemData['url'], true) === true) { + unset($menu[$group][$subMenuElementName]['children'][$menuItem]); + continue; + } + } + $menu[$group][$subMenuElementName]['children'] = array_values($menu[$group][$subMenuElementName]['children']); + if (empty($menu[$group][$subMenuElementName]['children'])) { + unset($subMenu[$subMenuElementName]); + } + } + } + if (empty($menu[$group])) { + unset($menu[$group]); + } + } + return $menu; + } } diff --git a/src/Model/Table/AppTable.php b/src/Model/Table/AppTable.php index baa82c3..3d3ae9d 100644 --- a/src/Model/Table/AppTable.php +++ b/src/Model/Table/AppTable.php @@ -12,259 +12,4 @@ class AppTable extends Table public function initialize(array $config): void { } - - public function getMenu() - { - $open = Configure::read('Cerebrate.open'); - return [ - 'ContactDB' => [ - 'Individuals' => [ - 'label' => __('Individuals'), - 'url' => '/individuals/index', - 'children' => [ - 'index' => [ - 'url' => '/individuals/index', - 'label' => __('List individuals') - ], - 'add' => [ - 'url' => '/individuals/add', - 'label' => __('Add individual') - ], - 'view' => [ - 'url' => '/individuals/view/{{id}}', - 'label' => __('View individual'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ], - 'edit' => [ - 'url' => '/individuals/edit/{{id}}', - 'label' => __('Edit individual'), - 'actions' => ['edit', 'delete', 'view'], - 'skipTopMenu' => 1 - ], - 'delete' => [ - 'url' => '/individuals/delete/{{id}}', - 'label' => __('Delete individual'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ], - 'Organisations' => [ - 'label' => __('Organisations'), - 'url' => '/organisations/index', - 'children' => [ - 'index' => [ - 'url' => '/organisations/index', - 'label' => __('List organisations') - ], - 'add' => [ - 'url' => '/organisations/add', - 'label' => __('Add organisation') - ], - 'view' => [ - 'url' => '/organisations/view/{{id}}', - 'label' => __('View organisation'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ], - 'edit' => [ - 'url' => '/organisations/edit/{{id}}', - 'label' => __('Edit organisation'), - 'actions' => ['edit', 'delete', 'view'], - 'skipTopMenu' => 1 - ], - 'delete' => [ - 'url' => '/organisations/delete/{{id}}', - 'label' => __('Delete organisation'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ], - 'EncryptionKeys' => [ - 'label' => __('Encryption keys'), - 'url' => '/encryptionKeys/index', - 'children' => [ - 'index' => [ - 'url' => '/encryptionKeys/index', - 'label' => __('List encryption keys') - ], - 'add' => [ - 'url' => '/encryptionKeys/add', - 'label' => __('Add encryption key') - ], - 'edit' => [ - 'url' => '/encryptionKeys/edit/{{id}}', - 'label' => __('Edit organisation'), - 'actions' => ['edit'], - 'skipTopMenu' => 1 - ] - ] - ] - ], - 'Trust Circles' => [ - 'SharingGroups' => [ - 'label' => __('Sharing Groups'), - 'url' => '/sharingGroups/index', - 'children' => [ - 'index' => [ - 'url' => '/sharingGroups/index', - 'label' => __('List sharing groups') - ], - 'add' => [ - 'url' => '/SharingGroups/add', - 'label' => __('Add sharing group') - ], - 'edit' => [ - 'url' => '/SharingGroups/edit/{{id}}', - 'label' => __('Edit sharing group'), - 'actions' => ['edit'], - 'skipTopMenu' => 1 - ] - ] - ] - ], - 'Administration' => [ - 'Roles' => [ - 'label' => __('Roles'), - 'url' => '/roles/index', - 'children' => [ - 'index' => [ - 'url' => '/roles/index', - 'label' => __('List roles') - ], - 'add' => [ - 'url' => '/roles/add', - 'label' => __('Add role') - ], - 'view' => [ - 'url' => '/roles/view/{{id}}', - 'label' => __('View role'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ], - 'edit' => [ - 'url' => '/roles/edit/{{id}}', - 'label' => __('Edit role'), - 'actions' => ['edit', 'delete', 'view'], - 'skipTopMenu' => 1 - ], - 'delete' => [ - 'url' => '/roles/delete/{{id}}', - 'label' => __('Delete role'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ], - 'Users' => [ - 'label' => __('Users'), - 'url' => '/users/index', - 'children' => [ - 'index' => [ - 'url' => '/users/index', - 'label' => __('List users') - ], - 'add' => [ - 'url' => '/users/add', - 'label' => __('Add user') - ], - 'view' => [ - 'url' => '/users/view/{{id}}', - 'label' => __('View user'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ], - 'edit' => [ - 'url' => '/users/edit/{{id}}', - 'label' => __('Edit user'), - 'actions' => ['edit', 'delete', 'view'], - 'skipTopMenu' => 1 - ], - 'delete' => [ - 'url' => '/users/delete/{{id}}', - 'label' => __('Delete user'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ], - 'MetaTemplates' => [ - 'label' => __('Meta Field Templates'), - 'url' => '/metaTemplates/index', - 'children' => [ - 'index' => [ - 'url' => '/metaTemplates/index', - 'label' => __('List Meta Templates') - ], - 'view' => [ - 'url' => '/metaTemplates/view/{{id}}', - 'label' => __('View Meta Template'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ], - 'delete' => [ - 'url' => '/metaTemplates/delete/{{id}}', - 'label' => __('Delete Meta Template'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ] - ], - 'Cerebrate' => [ - 'Roles' => [ - 'label' => __('Roles'), - 'url' => '/roles/index', - 'children' => [ - 'index' => [ - 'url' => '/roles/index', - 'label' => __('List roles') - ], - 'view' => [ - 'url' => '/roles/view/{{id}}', - 'label' => __('View role'), - 'actions' => ['delete', 'edit', 'view'], - 'skipTopMenu' => 1 - ] - ] - ], - 'Instance' => [ - __('Instance'), - 'url' => '/instance/home', - 'children' => [ - 'home' => [ - 'url' => '/instance/home', - 'label' => __('Home') - ] - ] - ] - ], - 'Open' => [ - 'Organisations' => [ - 'label' => __('Organisations'), - 'url' => '/open/organisations/index', - 'children' => [ - 'index' => [ - 'url' => '/open/organisations/index', - 'label' => __('List organisations') - ], - ], - 'open' => in_array('organisations', Configure::read('Cerebrate.open')) - ], - 'Individuals' => [ - 'label' => __('Individuals'), - 'url' => '/open/individuals/index', - 'children' => [ - 'index' => [ - 'url' => '/open/individuals/index', - 'label' => __('List individuals') - ], - ], - 'open' => in_array('individuals', Configure::read('Cerebrate.open')) - ] - ] - ]; - } }