From bdb005e341bbd03c9853f77d3d16f113c5c450c2 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 21 Jun 2020 21:27:11 +0200 Subject: [PATCH] new: [auth] Added authentication --- app/src/Application.php | 46 +- app/src/Controller/AppController.php | 29 +- app/src/Controller/Component/ACLComponent.php | 712 ++---------------- app/src/Controller/UsersController.php | 40 +- app/src/Model/Entity/User.php | 8 +- app/src/Model/Table/UsersTable.php | 8 +- app/templates/Users/add.php | 11 +- app/templates/Users/index.php | 20 +- 8 files changed, 216 insertions(+), 658 deletions(-) diff --git a/app/src/Application.php b/app/src/Application.php index 6c47b27..90e3865 100644 --- a/app/src/Application.php +++ b/app/src/Application.php @@ -23,6 +23,11 @@ use Cake\Http\BaseApplication; use Cake\Http\MiddlewareQueue; use Cake\Routing\Middleware\AssetMiddleware; use Cake\Routing\Middleware\RoutingMiddleware; +use Authentication\AuthenticationService; +use Authentication\AuthenticationServiceInterface; +use Authentication\AuthenticationServiceProviderInterface; +use Authentication\Middleware\AuthenticationMiddleware; +use Psr\Http\Message\ServerRequestInterface; /** * Application setup class. @@ -30,7 +35,7 @@ use Cake\Routing\Middleware\RoutingMiddleware; * This defines the bootstrapping logic and middleware layers you * want to use in your application. */ -class Application extends BaseApplication +class Application extends BaseApplication implements AuthenticationServiceProviderInterface { /** * Load all the application configuration and bootstrap logic. @@ -53,7 +58,7 @@ class Application extends BaseApplication if (Configure::read('debug')) { $this->addPlugin('DebugKit'); } - + $this->addPlugin('Authentication'); // Load more plugins here } @@ -81,8 +86,8 @@ class Application extends BaseApplication // creating the middleware instance specify the cache config name by // using it's second constructor argument: // `new RoutingMiddleware($this, '_cake_routes_')` - ->add(new RoutingMiddleware($this)); - + ->add(new RoutingMiddleware($this)) + ->add(new AuthenticationMiddleware($this)); return $middlewareQueue; } @@ -105,4 +110,37 @@ class Application extends BaseApplication // Load more plugins here } + + /** + * Returns a service provider instance. + * + * @param \Psr\Http\Message\ServerRequestInterface $request Request + * @return \Authentication\AuthenticationServiceInterface + */ + public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface + { + $service = new AuthenticationService(); + + // Define where users should be redirected to when they are not authenticated + $service->setConfig([ + 'unauthenticatedRedirect' => '/users/login', + 'queryParam' => 'redirect', + ]); + + $fields = [ + 'username' => 'username', + 'password' => 'password' + ]; + // Load the authenticators. Session should be first. + $service->loadAuthenticator('Authentication.Session'); + $service->loadAuthenticator('Authentication.Form', [ + 'fields' => $fields, + 'loginUrl' => '/users/login' + ]); + + // Load identifiers + $service->loadIdentifier('Authentication.Password', compact('fields')); + + return $service; + } } diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php index 45a22d2..b464e3f 100644 --- a/app/src/Controller/AppController.php +++ b/app/src/Controller/AppController.php @@ -39,6 +39,7 @@ class AppController extends Controller public $isRest = null; public $restResponsePayload = null; + public $user = null; /** * Initialization hook method. @@ -56,7 +57,7 @@ class AppController extends Controller $this->loadComponent('RequestHandler'); $this->loadComponent('Flash'); $this->loadComponent('RestResponse'); - $this->loadComponent('ACL'); + $this->loadComponent('Security'); $this->loadComponent('ParamHandler', [ 'request' => $this->request ]); @@ -64,7 +65,11 @@ class AppController extends Controller 'request' => $this->request, 'table' => $this->{$this->modelClass} ]); - + $this->loadComponent('Authentication.Authentication'); + $this->loadComponent('ACL', [ + 'request' => $this->request, + 'Authentication' => $this->Authentication + ]); if (Configure::read('debug')) { Configure::write('DebugKit.panels', ['DebugKit.Packages' => true]); Configure::write('DebugKit.forceEnable', true); @@ -80,6 +85,21 @@ class AppController extends Controller public function beforeFilter(EventInterface $event) { $this->isAdmin = true; + $this->ACL->setPublicInterfaces(); + if (!empty($this->request->getAttribute('identity'))) { + $this->loadModel('Users'); + $user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [ + 'contain' => ['Roles', 'Individuals' => 'Organisations'] + ]); + if (!empty($user['disabled'])) { + $this->Authentication->logout(); + $this->Flash->error(__('The user account is disabled.')); + return $this->redirect(['controller' => 'Users', 'action' => 'login']); + } + unset($user['password']); + $this->ACL->setUser($user); + } + $this->ACL->checkAccess(); $this->set('menu', $this->{$this->modelClass}->getMenu()); $this->set('ajax', $this->request->is('ajax')); } @@ -94,4 +114,9 @@ class AppController extends Controller { return true; } + + public function queryACL() + { + $this->ACL->findMissingFunctionNames(); + } } diff --git a/app/src/Controller/Component/ACLComponent.php b/app/src/Controller/Component/ACLComponent.php index 8fddcdf..b8f83ab 100644 --- a/app/src/Controller/Component/ACLComponent.php +++ b/app/src/Controller/Component/ACLComponent.php @@ -3,627 +3,44 @@ namespace App\Controller\Component; use Cake\Controller\Component; +use App\Model\Entity\User; +use Cake\Http\Exception\NotFoundException; +use Cake\Http\Exception\MethodNotAllowedException; +use Cake\Http\Exception\ForbiddenException; +use Cake\ORM\TableRegistry; class ACLComponent extends Component { + private $user = null; + + public function initialize(array $config): void + { + $this->request = $config['request']; + $this->Authentication = $config['Authentication']; + } + // syntax: // $__aclList[$controller][$action] = $permission_rules // $controller == '*' - any controller can have this action - // $action == array() - site admin only has access + // $action == [] - site admin only has access // $action == '*' - any role has access - // $action == array('OR' => array()) - any role in the array has access - // $action == array('AND' => array()) - roles with all permissions in the array have access + // $action == array('OR' => []) - any role in the array has access + // $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( - '*' => array( - 'blackhole' => array(), - 'checkAction' => array(), - 'checkAuthUser' => array(), - 'checkExternalAuthUser' => array(), - 'cleanModelCaches' => array(), - 'debugACL' => array(), - 'generateCount' => array(), - 'getActions' => array(), - 'pruneDuplicateUUIDs' => array(), - 'queryACL' => array(), - 'removeDuplicateEvents' => array(), - 'restSearch' => array('*'), - 'updateDatabase' => array(), - 'upgrade2324' => array(), - ), - 'attributes' => array( - 'add' => array('perm_add'), - 'add_attachment' => array('perm_add'), - 'add_threatconnect' => array('perm_add'), - 'addTag' => array('perm_tagger'), - 'attributeReplace' => array('perm_add'), - 'attributeStatistics' => array('*'), - 'bro' => array('*'), - 'checkAttachments' => array(), - 'checkComposites' => array('perm_admin'), - 'checkOrphanedAttributes' => array(), - 'delete' => array('perm_add'), - 'deleteSelected' => array('perm_add'), - 'describeTypes' => array('*'), - 'download' => array('*'), - 'downloadAttachment' => array('*'), - 'downloadSample' => array('*'), - 'edit' => array('perm_add'), - 'editField' => array('perm_add'), - 'editSelected' => array('perm_add'), - 'exportSearch' => array('*'), - 'fetchEditForm' => array('perm_add'), - 'fetchViewValue' => array('*'), - 'generateCorrelation' => array(), - 'hoverEnrichment' => array('perm_add'), - 'index' => array('*'), - 'pruneOrphanedAttributes' => array(), - 'removeTag' => array('perm_tagger'), - 'reportValidationIssuesAttributes' => array(), - 'restore' => array('perm_add'), - 'restSearch' => array('*'), - 'returnAttributes' => array('*'), - 'rpz' => array('*'), - 'search' => array('*'), - 'searchAlternate' => array('*'), - 'toggleCorrelation' => array('perm_add'), - 'text' => array('*'), - 'toggleToIDS' => array('perm_add'), - 'updateAttributeValues' => array('perm_add'), - 'view' => array('*'), - 'viewPicture' => array('*'), - ), - 'dashboards' => array( - 'getForm' => array('*'), - 'index' => array('*'), - 'updateSettings' => array('*'), - 'getEmptyWidget' => array('*'), - 'renderWidget' => array('*'), - 'listTemplates' => array('*'), - 'saveTemplate' => array('*'), - 'export' => array('*'), - 'import' => array('*'), - 'deleteTemplate' => array('*') - ), - 'decayingModel' => array( - "update" => array(), - "export" => array('*'), - "import" => array('*'), - "view" => array('*'), - "index" => array('*'), - "add" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "edit" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "delete" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "enable" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "disable" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "decayingTool" => array( 'OR' => array('perm_admin', 'perm_decaying')), - "getAllDecayingModels" => array('*'), - "decayingToolBasescore" => array('*'), - "decayingToolSimulation" => array('*'), - "decayingToolRestSearch" => array('*'), - "decayingToolComputeSimulation" => array('*') - ), - 'decayingModelMapping' => array( - "viewAssociatedTypes" => array('*'), - "linkAttributeTypeToModel" => array( 'OR' => array('perm_admin', 'perm_decaying')) - ), - 'communities' => array( - 'index' => array(), - 'requestAccess' => array(), - 'view' => array() - ), - 'eventBlacklists' => array( - 'add' => array(), - 'delete' => array(), - 'edit' => array(), - 'index' => array(), - 'massDelete' => array() - ), - 'eventDelegations' => array( - 'acceptDelegation' => array('perm_add'), - 'delegateEvent' => array('perm_delegate'), - 'deleteDelegation' => array('perm_add'), - 'index' => array('*'), - 'view' => array('*'), - ), - 'events' => array( - 'add' => array('perm_add'), - 'addIOC' => array('perm_add'), - 'addTag' => array('perm_tagger'), - 'add_misp_export' => array('perm_modify'), - 'alert' => array('perm_publish'), - 'automation' => array('perm_auth'), - 'checkLocks' => array('perm_add'), - 'checkPublishedStatus' => array('*'), - 'checkuuid' => array('perm_sync'), - 'contact' => array('*'), - 'csv' => array('*'), - 'cullEmptyEvents' => array(), - 'delegation_index' => array('*'), - 'delete' => array('perm_add'), - 'deleteNode' => array('*'), - 'dot' => array(), - 'downloadExport' => array('*'), - 'downloadOpenIOCEvent' => array('*'), - 'edit' => array('perm_add'), - 'enrichEvent' => array('perm_add'), - 'export' => array('*'), - 'exportChoice' => array('*'), - 'exportModule' => array('*'), - 'filterEventIdsForPush' => array('perm_sync'), - 'filterEventIndex' => array('*'), - 'freeTextImport' => array('perm_add'), - 'getEditStrategy' => array('perm_add'), - 'getEventInfoById' => array('*'), - 'getEventGraphReferences' => array('*'), - 'getEventGraphTags' => array('*'), - 'getEventGraphGeneric' => array('*'), - 'getEventTimeline' => array('*'), - 'genDistributionGraph' => array('*'), - 'getDistributionGraph' => array('*'), - 'getReferenceData' => array('*'), - 'getReferences' => array('*'), - 'getObjectTemplate' => array('*'), - 'handleModuleResults' => array('*'), - 'hids' => array('*'), - 'index' => array('*'), - 'importChoice' => array('*'), - 'importModule' => array('*'), - 'massDelete' => array('perm_site_admin'), - 'merge' => array('perm_modify'), - 'nids' => array('*'), - 'proposalEventIndex' => array('*'), - 'publish' => array('perm_publish'), - 'publishSightings' => array('perm_sighting'), - 'pushEventToZMQ' => array('perm_publish_zmq'), - 'pushEventToKafka' => array('perm_publish_kafka'), - 'pushProposals' => array('perm_sync'), - 'queryEnrichment' => array('perm_add'), - 'removePivot' => array('*'), - 'removeTag' => array('perm_tagger'), - 'reportValidationIssuesEvents' => array(), - 'restSearch' => array('*'), - 'saveFreeText' => array('perm_add'), - 'stix' => array('*'), - 'stix2' => array('*'), - 'strposarray' => array(), - 'toggleCorrelation' => array('perm_add'), - 'unpublish' => array('perm_modify'), - 'updateGraph' => array('*'), - 'upload_analysis_file' => array('perm_add'), - 'upload_sample' => array('AND' => array('perm_auth', 'perm_add')), - 'upload_stix' => array('perm_add'), - 'view' => array('*'), - 'viewEventAttributes' => array('*'), - 'viewEventGraph' => array('*'), - 'viewGraph' => array('*'), - 'viewGalaxyMatrix' => array('*'), - 'xml' => array('*') - ), - 'favouriteTags' => array( - 'toggle' => array('*'), - 'getToggleField' => array('*') - ), - 'feeds' => array( - 'add' => array(), - 'cacheFeeds' => array(), - 'compareFeeds' => array('*'), - 'delete' => array(), - 'disable' => array(), - 'edit' => array(), - 'enable' => array(), - 'feedCoverage' => array('*'), - 'fetchFromAllFeeds' => array(), - 'fetchFromFeed' => array(), - 'fetchSelectedFromFreetextIndex' => array(), - 'getEvent' => array(), - 'importFeeds' => array(), - 'index' => array('*'), - 'loadDefaultFeeds' => array('perm_site_admin'), - 'previewEvent' => array('*'), - 'previewIndex' => array('*'), - 'searchCaches' => array('*'), - 'toggleSelected' => array('perm_site_admin'), - 'view' => array('*'), - ), - 'galaxies' => array( - 'attachCluster' => array('perm_tagger'), - 'attachMultipleClusters' => array('perm_tagger'), - 'delete' => array(), - 'index' => array('*'), - 'selectGalaxy' => array('perm_tagger'), - 'selectGalaxyNamespace' => array('perm_tagger'), - 'selectCluster' => array('perm_tagger'), - 'showGalaxies' => array('*'), - 'update' => array(), - 'view' => array('*'), - 'viewGraph' => array('*') - ), - 'galaxyClusters' => array( - 'attachToEvent' => array('perm_tagger'), - 'delete' => array('perm_site_admin'), - 'detach' => array('perm_tagger'), - 'index' => array('*'), - 'view' => array('*'), - 'viewGalaxyMatrix' => array('*') - ), - 'galaxyElements' => array( - 'index' => array('*') - ), - 'jobs' => array( - 'cache' => array('*'), - 'getError' => array(), - 'getGenerateCorrelationProgress' => array('*'), - 'getProgress' => array('*'), - 'index' => array(), - 'clearJobs' => array() - ), - 'logs' => array( - 'admin_index' => array('perm_audit'), - 'admin_search' => array('perm_audit'), - 'event_index' => array('*'), - 'maxDateActivity' => array('*'), - 'returnDates' => array('*'), - 'testForStolenAttributes' => array(), - 'pruneUpdateLogs' => array() - ), - 'modules' => array( - 'index' => array('perm_auth'), - 'queryEnrichment' => array('perm_auth'), - ), - 'news' => array( - 'add' => array(), - 'edit' => array(), - 'delete' => array(), - 'index' => array('*'), - ), - 'noticelists' => array( - 'delete' => array(), - 'enableNoticelist' => array(), - 'getToggleField' => array(), - 'index' => array('*'), - 'toggleEnable' => array(), - 'update' => array(), - 'view' => array('*') - ), - 'objects' => array( - 'add' => array('perm_add'), - 'addValueField' => array('perm_add'), - 'delete' => array('perm_add'), - 'edit' => array('perm_add'), - 'get_row' => array('perm_add'), - 'orphanedObjectDiagnostics' => array(), - 'editField' => array('perm_add'), - 'fetchEditForm' => array('perm_add'), - 'fetchViewValue' => array('*'), - 'quickAddAttributeForm' => array('perm_add'), - 'quickFetchTemplateWithValidObjectAttributes' => array('perm_add'), - 'restSearch' => array('*'), - 'proposeObjectsFromAttributes' => array('*'), - 'groupAttributesIntoObject' => array('perm_add'), - 'revise_object' => array('perm_add'), - 'view' => array('*'), - ), - 'objectReferences' => array( - 'add' => array('perm_add'), - 'delete' => array('perm_add'), - 'view' => array('*'), - ), - 'objectTemplates' => array( - 'activate' => array(), - 'add' => array('perm_object_template'), - 'edit' => array('perm_object_template'), - 'delete' => array('perm_object_template'), - 'getToggleField' => array(), - 'objectChoice' => array('*'), - 'objectMetaChoice' => array('perm_add'), - 'view' => array('*'), - 'viewElements' => array('*'), - 'index' => array('*'), - 'update' => array('perm_site_admin') - ), - 'objectTemplateElements' => array( - 'viewElements' => array('*') - ), - 'orgBlacklists' => array( - 'add' => array(), - 'delete' => array(), - 'edit' => array(), - 'index' => array(), - ), - 'organisations' => array( - 'admin_add' => array(), - 'admin_delete' => array(), - 'admin_edit' => array(), - 'admin_generateuuid' => array(), - 'admin_merge' => array(), - 'fetchOrgsForSG' => array('*'), - 'fetchSGOrgRow' => array('*'), - 'getUUIDs' => array('perm_sync'), - 'index' => array('*'), - 'landingpage' => array('*'), - 'view' => array('*'), - ), - 'pages' => array( - 'display' => array('*'), - ), - 'posts' => array( - 'add' => array('*'), - 'delete' => array('*'), - 'edit' => array('*'), - 'pushMessageToZMQ' => array('perm_site_admin') - ), - 'regexp' => array( - 'admin_add' => array('perm_regexp_access'), - 'admin_clean' => array('perm_regexp_access'), - 'admin_delete' => array('perm_regexp_access'), - 'admin_edit' => array('perm_regexp_access'), - 'admin_index' => array('perm_regexp_access'), - 'cleanRegexModifiers' => array('perm_regexp_access'), - 'index' => array('*'), - ), - 'restClientHistory' => array( - 'delete' => array('*'), - 'index' => array('*') - ), - 'roles' => array( - 'admin_add' => array(), - 'admin_delete' => array(), - 'admin_edit' => array(), - 'admin_index' => array('perm_admin'), - 'admin_set_default' => array(), - 'index' => array('*'), - 'view' => array('*'), - ), - 'servers' => array( - 'add' => array(), - 'dbSchemaDiagnostic' => array(), - 'cache' => array(), - 'changePriority' => array(), - 'checkout' => array(), - 'clearWorkerQueue' => array(), - 'createSync' => array('perm_sync'), - 'delete' => array(), - 'deleteFile' => array(), - 'edit' => array(), - 'fetchServersForSG' => array('*'), - 'filterEventIndex' => array(), - 'getApiInfo' => array('*'), - 'getGit' => array(), - 'getInstanceUUID' => array('perm_sync'), - 'getPyMISPVersion' => array('*'), - 'getRemoteUser' => array(), - 'getSetting' => array(), - 'getSubmodulesStatus' => array(), - 'getSubmoduleQuickUpdateForm' => array(), - 'getWorkers' => array(), - 'getVersion' => array('*'), - 'import' => array(), - 'index' => array(), - 'ondemandAction' => array(), - 'postTest' => array('perm_sync'), - 'previewEvent' => array(), - 'previewIndex' => array(), - 'pull' => array(), - 'purgeSessions' => array(), - 'push' => array(), - 'releaseUpdateLock' => array(), - 'resetRemoteAuthKey' => array(), - 'rest' => array('perm_auth'), - 'restartDeadWorkers' => array(), - 'restartWorkers' => array(), - 'serverSettings' => array(), - 'serverSettingsEdit' => array(), - 'serverSettingsReloadSetting' => array(), - 'startWorker' => array(), - 'startZeroMQServer' => array(), - 'statusZeroMQServer' => array(), - 'stopWorker' => array(), - 'stopZeroMQServer' => array(), - 'testConnection' => array(), - 'update' => array(), - 'updateJSON' => array(), - 'updateProgress' => array(), - 'updateSubmodule' => array(), - 'uploadFile' => array(), - 'viewDeprecatedFunctionUse' => array() - ), - 'shadowAttributes' => array( - 'accept' => array('perm_add'), - 'acceptSelected' => array('perm_add'), - 'add' => array('perm_add'), - 'add_attachment' => array('perm_add'), - 'delete' => array('perm_add'), - 'discard' => array('perm_add'), - 'discardSelected' => array('perm_add'), - 'download' => array('*'), - 'edit' => array('perm_add'), - 'editField' => array('perm_add'), - 'fetchEditForm' => array('perm_add'), - 'generateCorrelation' => array(), - 'getProposalsByUuid' => array('perm_sync'), - 'getProposalsByUuidList' => array('perm_sync'), - 'index' => array('*'), - 'view' => array('*'), - ), - 'sharingGroups' => array( - 'add' => array('perm_sharing_group'), - 'addServer' => array('perm_sharing_group'), - 'addOrg' => array('perm_sharing_group'), - 'delete' => array('perm_sharing_group'), - 'edit' => array('perm_sharing_group'), - 'index' => array('*'), - 'removeServer' => array('perm_sharing_group'), - 'removeOrg' => array('perm_sharing_group'), - 'view' => array('*'), - ), - 'sightings' => array( - 'add' => array('perm_sighting'), - 'restSearch' => array('perm_sighting'), - 'advanced' => array('perm_sighting'), - 'delete' => array('perm_sighting'), - 'index' => array('*'), - 'listSightings' => array('*'), - 'quickDelete' => array('perm_sighting'), - 'viewSightings' => array('*'), - 'bulkSaveSightings' => array('OR' => array('perm_sync', 'perm_sighting')), - 'quickAdd' => array('perm_sighting') - ), - 'sightingdb' => array( - 'add' => array(), - 'edit' => array(), - 'delete' => array(), - 'index' => array(), - 'requestStatus' => array(), - 'search' => array() - ), - 'tagCollections' => array( - 'add' => array('perm_tag_editor'), - 'addTag' => array('perm_tag_editor'), - 'delete' => array('perm_tag_editor'), - 'edit' => array('perm_tag_editor'), - 'getRow' => array('perm_tag_editor'), - 'import' => array('perm_tag_editor'), - 'index' => array('*'), - 'removeTag' => array('perm_tag_editor'), - 'view' => array('*') - ), - 'tags' => array( - 'add' => array('perm_tag_editor'), - 'attachTagToObject' => array('perm_tagger'), - 'delete' => array(), - 'edit' => array(), - 'index' => array('*'), - 'quickAdd' => array('perm_tag_editor'), - 'removeTagFromObject' => array('perm_tagger'), - 'search' => array('*'), - 'selectTag' => array('perm_tagger'), - 'selectTaxonomy' => array('perm_tagger'), - 'showEventTag' => array('*'), - 'showAttributeTag' => array('*'), - 'showTagControllerTag' => array('*'), - 'tagStatistics' => array('*'), - 'view' => array('*'), - 'viewGraph' => array('*'), - 'viewTag' => array('*') - ), - 'tasks' => array( - 'index' => array(), - 'setTask' => array(), - ), - 'taxonomies' => array( - 'addTag' => array(), - 'delete' => array(), - 'disable' => array(), - 'disableTag' => array(), - 'enable' => array(), - 'index' => array('*'), - 'taxonomyMassConfirmation' => array('perm_tagger'), - 'taxonomyMassHide' => array('perm_tagger'), - 'taxonomyMassUnhide' => array('perm_tagger'), - 'toggleRequired' => array('perm_site_admin'), - 'update' => array(), - 'view' => array('*'), - 'unhideTag' => array('perm_tagger'), - 'hideTag' => array('perm_tagger'), - ), - 'templateElements' => array( - 'add' => array('perm_template'), - 'delete' => array('perm_template'), - 'edit' => array('perm_template'), - 'index' => array('*'), - 'templateElementAddChoices' => array('perm_template'), - ), - 'templates' => array( - 'add' => array('perm_template'), - 'delete' => array('perm_template'), - 'deleteTemporaryFile' => array('perm_add'), - 'edit' => array('perm_template'), - 'index' => array('*'), - 'populateEventFromTemplate' => array('perm_add'), - 'saveElementSorting' => array('perm_template'), - 'submitEventPopulation' => array('perm_add'), - 'templateChoices' => array('*'), - 'uploadFile' => array('*'), - 'view' => array('*'), - ), - 'threads' => array( - 'index' => array('*'), - 'view' => array('*'), - 'viewEvent' => array('*'), - ), - 'users' => array( - 'acceptRegistrations' => array('perm_site_admin'), - 'admin_add' => array('perm_admin'), - 'admin_delete' => array('perm_admin'), - 'admin_edit' => array('perm_admin'), - 'admin_email' => array('perm_admin'), - 'admin_filterUserIndex' => array('perm_admin'), - 'admin_index' => array('perm_admin'), - 'admin_monitor' => array('perm_site_admin'), - 'admin_quickEmail' => array('perm_admin'), - 'admin_view' => array('perm_admin'), - 'attributehistogram' => array('*'), - 'change_pw' => array('*'), - 'checkAndCorrectPgps' => array(), - 'checkIfLoggedIn' => array('*'), - 'dashboard' => array('*'), - 'delete' => array('perm_admin'), - 'discardRegistrations' => array('perm_site_admin'), - 'downloadTerms' => array('*'), - 'edit' => array('*'), - 'email_otp' => array('*'), - 'searchGpgKey' => array('*'), - 'fetchGpgKey' => array('*'), - 'histogram' => array('*'), - 'initiatePasswordReset' => array('perm_admin'), - 'login' => array('*'), - 'logout' => array('*'), - 'register' => array('*'), - 'registrations' => array('perm_site_admin'), - 'resetAllSyncAuthKeys' => array(), - 'resetauthkey' => array('*'), - 'request_API' => array('*'), - 'routeafterlogin' => array('*'), - 'statistics' => array('*'), - 'tagStatisticsGraph' => array('*'), - 'terms' => array('*'), - 'updateLoginTime' => array('*'), - 'verifyCertificate' => array(), - 'verifyGPG' => array(), - 'view' => array('*'), - ), - 'userSettings' => array( - 'index' => array('*'), - 'view' => array('*'), - 'setSetting' => array('*'), - 'getSetting' => array('*'), - 'delete' => array('*'), - 'setHomePage' => array('*') - ), - 'warninglists' => array( - 'checkValue' => array('perm_auth'), - 'delete' => array(), - 'enableWarninglist' => array(), - 'getToggleField' => array(), - 'index' => array('*'), - 'toggleEnable' => array(), - 'update' => array(), - 'view' => array('*') - ), - 'whitelists' => array( - 'admin_add' => array('perm_regexp_access'), - 'admin_delete' => array('perm_regexp_access'), - 'admin_edit' => array('perm_regexp_access'), - 'admin_index' => array('perm_regexp_access'), - 'index' => array('*'), - ), - 'eventGraph' => array( - 'view' => array('*'), - 'add' => array('perm_add'), - 'delete' => array('perm_modify'), - ) + private $aclList = array( + 'Pages' => [ + 'display' => ['*'] + ], + 'Users' => [ + 'add' => ['perm_admin'], + 'delete' => ['perm_admin'], + 'edit' => ['*'], + 'index' => ['perm_admin'], + 'login' => ['*'], + 'logout' => ['*'], + 'view' => ['*'] + ] ); private function __checkLoggedActions($user, $controller, $action) @@ -681,7 +98,7 @@ class ACLComponent extends Component } } if ($hit) { - $this->Log = ClassRegistry::init('Log'); + $this->Log = TableRegistry::get('Log'); $this->Log->create(); $this->Log->save(array( 'org' => 'SYSTEM', @@ -698,43 +115,58 @@ class ACLComponent extends Component } } + public function setUser(User $user): void + { + $this->user = $user; + } + + public function getUser(): User + { + return $this->user; + } + + /* + * By default nothing besides the login is public. If configured, override the list with the additional interfaces + */ + public function setPublicInterfaces(): void + { + $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($user, $controller, $action, $soft = false) + public function checkAccess(bool $soft = false): bool { - $controller = lcfirst(Inflector::camelize($controller)); - $action = strtolower($action); - $aclList = $this->__aclList; - foreach ($aclList as $k => $v) { - $aclList[$k] = array_change_key_case($v); - } - $this->__checkLoggedActions($user, $controller, $action); - if ($user['Role']['perm_site_admin']) { + $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; } - if (!isset($aclList[$controller])) { - return $this->__error(404, 'Invalid controller.', $soft); - } - if ($user['Role']['perm_site_admin']) { + if (!empty($this->user->role->perm_admin)) { return true; } - if (isset($aclList[$controller][$action]) && !empty($aclList[$controller][$action])) { - if (in_array('*', $aclList[$controller][$action])) { + //$this->__checkLoggedActions($user, $controller, $action); + if (!isset($this->aclList[$controller])) { + return $this->__error(404, __('Invalid controller.'), $soft); + } + if (isset($this->aclList[$controller][$action]) && !empty($this->aclList[$controller][$action])) { + if (in_array('*', $this->aclList[$controller][$action])) { return true; } - if (isset($aclList[$controller][$action]['OR'])) { - foreach ($aclList[$controller][$action]['OR'] as $permission) { + if (isset($this->aclList[$controller][$action]['OR'])) { + foreach ($this->aclList[$controller][$action]['OR'] as $permission) { if ($user['Role'][$permission]) { return true; } } - } elseif (isset($aclList[$controller][$action]['AND'])) { + } elseif (isset($this->aclList[$controller][$action]['AND'])) { $allConditionsMet = true; - foreach ($aclList[$controller][$action]['AND'] as $permission) { + foreach ($this->aclList[$controller][$action]['AND'] as $permission) { if (!$user['Role'][$permission]) { $allConditionsMet = false; } @@ -742,8 +174,6 @@ class ACLComponent extends Component if ($allConditionsMet) { return true; } - } elseif ($user['Role'][$aclList[$controller][$action][0]]) { - return true; } } return $this->__error(403, 'You do not have permission to use this functionality.', $soft); @@ -770,13 +200,13 @@ class ACLComponent extends Component $functionFinder = '/function[\s\n]+(\S+)[\s\n]*\(/'; $dir = new Folder(APP . 'Controller'); $files = $dir->find('.*\.php'); - $results = array(); + $results = []; foreach ($files as $file) { $controllerName = lcfirst(str_replace('Controller.php', "", $file)); if ($controllerName === 'app') { $controllerName = '*'; } - $functionArray = array(); + $functionArray = []; $fileContents = file_get_contents(APP . 'Controller' . DS . $file); $fileContents = preg_replace('/\/\*[^\*]+?\*\//', '', $fileContents); preg_match_all($functionFinder, $fileContents, $functionArray); @@ -799,7 +229,7 @@ class ACLComponent extends Component public function findMissingFunctionNames($content = false) { $results = $this->__findAllFunctions(); - $missing = array(); + $missing = []; foreach ($results as $controller => $functions) { foreach ($functions as $function) { if (!isset($this->__aclList[$controller]) @@ -813,9 +243,9 @@ class ACLComponent extends Component public function printRoleAccess($content = false) { - $results = array(); - $this->Role = ClassRegistry::init('Role'); - $conditions = array(); + $results = []; + $this->Role = TableRegistry::get('Role'); + $conditions = []; if (is_numeric($content)) { $conditions = array('Role.id' => $content); } @@ -835,7 +265,7 @@ class ACLComponent extends Component private function __checkRoleAccess($role) { - $result = 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) { diff --git a/app/src/Controller/UsersController.php b/app/src/Controller/UsersController.php index 071e537..c3426eb 100644 --- a/app/src/Controller/UsersController.php +++ b/app/src/Controller/UsersController.php @@ -38,8 +38,11 @@ class UsersController extends AppController $this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate'); } - public function view($id) + public function view($id = false) { + if (empty($id) || empty($this->ACL->getUser()['role']['perm_admin'])) { + $id = $this->ACL->getUser()['id']; + } $this->CRUD->view($id, [ 'contain' => ['Individuals' => ['Alignments' => 'Organisations'], 'Roles'] ]); @@ -49,9 +52,16 @@ class UsersController extends AppController $this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate'); } - public function edit($id) + public function edit($id = false) { - $this->CRUD->edit($id); + if (empty($id) || empty($this->ACL->getUser()['role']['perm_admin'])) { + $id = $this->ACL->getUser()['id']; + } + $this->CRUD->edit($id, [ + 'get' => [ + 'fields' => ['id', 'individual_id', 'role_id', 'username', 'disabled'] + ] + ]); if ($this->ParamHandler->isRest()) { return $this->restResponsePayload; } @@ -76,4 +86,28 @@ class UsersController extends AppController } $this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate'); } + + public function login() + { + $result = $this->Authentication->getResult(); + // If the user is logged in send them away. + if ($result->isValid()) { + $target = $this->Authentication->getLoginRedirect() ?? '/instance/home'; + return $this->redirect($target); + } + if ($this->request->is('post') && !$result->isValid()) { + $this->Flash->error(__('Invalid username or password')); + } + $this->viewBuilder()->setLayout('login'); + } + + public function logout() + { + $result = $this->Authentication->getResult(); + if ($result->isValid()) { + $this->Authentication->logout(); + $this->Flash->success(__('Goodbye.')); + return $this->redirect(['controller' => 'Users', 'action' => 'login']); + } + } } diff --git a/app/src/Model/Entity/User.php b/app/src/Model/Entity/User.php index 585debd..b69d144 100644 --- a/app/src/Model/Entity/User.php +++ b/app/src/Model/Entity/User.php @@ -4,8 +4,14 @@ namespace App\Model\Entity; use App\Model\Entity\AppModel; use Cake\ORM\Entity; +use Authentication\PasswordHasher\DefaultPasswordHasher; class User extends AppModel { - + protected function _setPassword(string $password) : ?string + { + if (strlen($password) > 0) { + return (new DefaultPasswordHasher())->hash($password); + } + } } diff --git a/app/src/Model/Table/UsersTable.php b/app/src/Model/Table/UsersTable.php index d012b4b..cdf6c2d 100644 --- a/app/src/Model/Table/UsersTable.php +++ b/app/src/Model/Table/UsersTable.php @@ -5,6 +5,7 @@ namespace App\Model\Table; use App\Model\Table\AppTable; use Cake\ORM\Table; use Cake\Validation\Validator; +use Cake\ORM\RulesChecker; class UsersTable extends AppTable { @@ -26,7 +27,7 @@ class UsersTable extends AppTable 'cascadeCallbacks' => false ] ); - $this->setDisplayField('id'); + $this->setDisplayField('username'); } public function validationDefault(Validator $validator): Validator @@ -35,4 +36,9 @@ class UsersTable extends AppTable ->requirePresence(['password'], 'create'); return $validator; } + + public function buildRules(RulesChecker $rules): RulesChecker + { + return $rules; + } } diff --git a/app/templates/Users/add.php b/app/templates/Users/add.php index d2c0054..b854a87 100644 --- a/app/templates/Users/add.php +++ b/app/templates/Users/add.php @@ -11,10 +11,7 @@ 'options' => $dropdownData['individual'] ], [ - 'field' => 'role_id', - 'type' => 'dropdown', - 'label' => __('Role'), - 'options' => $dropdownData['role'] + 'field' => 'username' ], [ 'field' => 'password', @@ -24,6 +21,12 @@ 'field' => 'confirm_password', 'label' => __('Confirm Password') ], + [ + 'field' => 'role_id', + 'type' => 'dropdown', + 'label' => __('Role'), + 'options' => $dropdownData['role'] + ], [ 'field' => 'disabled', 'type' => 'checkbox', diff --git a/app/templates/Users/index.php b/app/templates/Users/index.php index 00a306b..e73e860 100644 --- a/app/templates/Users/index.php +++ b/app/templates/Users/index.php @@ -5,6 +5,17 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'top_bar' => [ 'pull' => 'right', 'children' => [ + [ + 'type' => 'simple', + 'children' => [ + 'data' => [ + 'type' => 'simple', + 'text' => __('Add User'), + 'class' => 'btn btn-primary', + 'popover_url' => '/users/add' + ] + ] + ], [ 'type' => 'search', 'button' => __('Filter'), @@ -20,6 +31,11 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'sort' => 'id', 'data_path' => 'id', ], + [ + 'name' => __('Username'), + 'sort' => 'username', + 'data_path' => 'username', + ], [ 'name' => __('Email'), 'sort' => 'individual.email', @@ -55,8 +71,8 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'icon' => 'eye' ], [ - 'url' => '/users/edit', - 'url_params_data_paths' => ['id'], + 'onclick' => 'populateAndLoadModal(\'/users/edit/[onclick_params_data_path]\');', + 'onclick_params_data_path' => 'id', 'icon' => 'edit' ], [