diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index 6cd9e93..0320195 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -1,6 +1,7 @@ scope, $toolName, $this->action); + $processingTemplatePath = sprintf('%s/%s/%s.php', $this->scope, $connectorName, $this->action); $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); if ($file->exists()) { $this->processingTemplate = str_replace('.php', '', $processingTemplatePath); @@ -40,10 +41,14 @@ class LocalToolRequestProcessor extends GenericRequestProcessor $file->close(); } - protected function validateToolName($requestData) + protected function validateConnectorName($requestData) { - if (empty($requestData['data']['toolName'])) { - throw new Exception('Error while validating request data. Tool name is missing.'); + if (empty($requestData['data']['connectorName'])) { + throw new NotFoundException('Error while validating request data. Connector name is missing.'); + } + $connector = $this->getConnectorFromClassname($requestData['data']['connectorName']); + if (is_null($connector)) { + throw new NotFoundException(__('Error while validating request data. Unkown connector `{0}`', $requestData['data']['connectorName'])); } } @@ -55,14 +60,24 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return $brood; } + protected function filterAlignmentsForBrood($individual, $brood) + { + foreach ($individual->alignments as $i => $alignment) { + if ($alignment->organisation_id != $brood->organisation_id) { + unset($individual->alignments[$i]); + } + } + return $individual; + } + protected function getConnector($request) { try { - $connectorClasses = $this->LocalTools->getConnectorByToolName($request->local_tool_name); + $connectorClasses = $this->LocalTools->getConnectors($request->local_tool_connector_name); if (!empty($connectorClasses)) { $connector = array_values($connectorClasses)[0]; } - } catch (Cake\Http\Exception\NotFoundException $e) { + } catch (NotFoundException $e) { $connector = null; } return $connector; @@ -71,23 +86,55 @@ class LocalToolRequestProcessor extends GenericRequestProcessor protected function getConnectorMeta($request) { try { - $connectorClasses = $this->LocalTools->getConnectorByToolName($request->local_tool_name); - if (!empty($connectorClasses)) { - $connectorMeta = $this->LocalTools->extractMeta($connectorClasses)[0]; - } - } catch (Cake\Http\Exception\NotFoundException $e) { - $connectorMeta = null; + $className = $request->local_tool_connector_name; + $connector = $this->getConnectorFromClassname($className); + $connectorMeta = $this->LocalTools->extractMeta([$className => $connector])[0]; + } catch (NotFoundException $e) { + $connectorMeta = []; } - return !is_null($connectorMeta) ? $connectorMeta : []; + return $connectorMeta; + } + + protected function getConnectorFromClassname($className) + { + try { + $connectorClasses = $this->LocalTools->getConnectors($className); + if (!empty($connectorClasses)) { + $connector = array_values($connectorClasses)[0]; + } + } catch (NotFoundException $e) { + $connector = null; + } + return $connector; + } + + protected function getConnectorMetaFromClassname($className) + { + try { + $connector = $this->getConnectorFromClassname($className); + $connectorMeta = $this->LocalTools->extractMeta([$className => $connector])[0]; + } catch (NotFoundException $e) { + $connectorMeta = []; + } + return $connectorMeta; + } + + protected function attachRequestAssociatedData($request) + { + $request->brood = $this->getIssuerBrood($request); + $request->connector = $this->getConnectorMeta($request); + $request->individual = $request->user->individual; + $request->individual = $this->filterAlignmentsForBrood($request->individual, $request->brood); + return $request; } protected function addBaseValidatorRules($validator) { return $validator - ->requirePresence('toolName') - ->notEmpty('toolName', 'A url must be provided') - ->requirePresence('url') // url -> cerebrate_url - ->notEmpty('url', 'A url must be provided'); + ->requirePresence('connectorName') + ->notEmpty('connectorName', 'The connector name must be provided') + ->requirePresence('cerebrateURL') + ->notEmpty('cerebrateURL', 'A url must be provided'); // ->add('url', 'validFormat', [ // 'rule' => 'url', // 'message' => 'URL must be valid' @@ -110,16 +157,16 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple } public function create($requestData) { - $this->validateToolName($requestData); + $this->validateConnectorName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Request for {0} Inter-connection', $requestData['local_tool_name']); + $connectorMeta = $this->getConnectorMetaFromClassname($requestData['data']['connectorName']); + $requestData['title'] = __('Request for {0} Inter-connection', $connectorMeta['name']); return parent::create($requestData); } public function getViewVariables($request) { - $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnectorMeta($request); + $request = $this->attachRequestAssociatedData($request); return [ 'request' => $request, 'progressStep' => 0, @@ -170,7 +217,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple protected function acceptConnection($connector, $remoteCerebrate, $requestData) { $connectorResult = $connector->acceptConnection($requestData['data']); - $connectorResult['toolName'] = $requestData->local_tool_name; + $connectorResult['connectorName'] = $requestData->local_tool_name; $response = $this->sendAcceptedRequestToRemote($remoteCerebrate, $connectorResult); // change state if sending fails // add the entry to the outbox if sending fails. @@ -180,7 +227,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple protected function declineConnection($connector, $remoteCerebrate, $requestData) { $connectorResult = $connector->declineConnection($requestData['data']); - $connectorResult['toolName'] = $requestData->local_tool_name; + $connectorResult['connectorName'] = $requestData->local_tool_name; $response = $this->sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult); return $response; } @@ -216,16 +263,16 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene } public function create($requestData) { - $this->validateToolName($requestData); + $this->validateConnectorName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Inter-connection for {0} has been accepted', $requestData['local_tool_name']); + $connectorMeta = $this->getConnectorMetaFromClassname($requestData['data']['connectorName']); + $requestData['title'] = __('Inter-connection for {0} has been accepted', $connectorMeta['name']); return parent::create($requestData); } public function getViewVariables($request) { - $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnectorMeta($request); + $request = $this->attachRequestAssociatedData($request); return [ 'request' => $request, 'progressStep' => 1, @@ -280,16 +327,16 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene } public function create($requestData) { - $this->validateToolName($requestData); + $this->validateConnectorName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Declined inter-connection for {0}', $requestData['local_tool_name']); + $connectorMeta = $this->getConnectorMetaFromClassname($requestData['data']['connectorName']); + $requestData['title'] = __('Declined inter-connection for {0}', $connectorMeta['name']); return parent::create($requestData); } public function getViewVariables($request) { - $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnectorMeta($request); + $request = $this->attachRequestAssociatedData($request); return [ 'request' => $request, 'progressStep' => 1, diff --git a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php index 72b8d94..df45df9 100644 --- a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -50,22 +50,38 @@ $footerButtons[] = [ $table = $this->Bootstrap->table(['small' => true, 'bordered' => false, 'striped' => false, 'hover' => false], [ 'fields' => [ + ['key' => 'created', 'label' => __('Date'), 'formatter' => function($value, $row) { + return $value->i18nFormat('yyyy-MM-dd HH:mm:ss'); + }], ['key' => 'connector', 'label' => __('Tool Name'), 'formatter' => function($connector, $row) { return sprintf('%s', $this->Url->build(['controller' => 'localTools', 'action' => 'viewConnector', $connector['name']]), sprintf('%s (v%s)', h($connector['name']), h($connector['connector_version'])) ); }], - ['key' => 'created', 'label' => __('Date'), 'formatter' => function($value, $row) { - return $value->i18nFormat('yyyy-MM-dd HH:mm:ss'); - }], - ['key' => 'origin', 'label' => __('Origin')], ['key' => 'brood', 'label' => __('Brood'), 'formatter' => function($brood, $row) { return sprintf('%s', $this->Url->build(['controller' => 'broods', 'action' => 'view', $brood['id']]), h($brood['name']) ); - }] + }], + ['key' => 'individual', 'label' => __('Individual'), 'formatter' => function($individual, $row) { + return sprintf('%s', + $this->Url->build(['controller' => 'users', 'action' => 'view', $individual['id']]), + h($individual['email']) + ); + }], + ['key' => 'individual.alignments', 'label' => __('Alignment'), 'formatter' => function($alignments, $row) { + $html = ''; + foreach ($alignments as $alignment) { + $html .= sprintf('
%s @ %s
', + h($alignment['type']), + $this->Url->build(['controller' => 'users', 'action' => 'view', $alignment['organisation']['id']]), + h($alignment['organisation']['name']) + ); + } + return $html; + }], ], 'items' => [$request->toArray()], ]); @@ -103,8 +119,8 @@ $bodyHtml = sprintf('
%s
%s
); echo $this->Bootstrap->modal([ - 'title' => __('Interconnection Request for {0}', h($request->data['toolName'])), - 'size' => 'lg', + 'title' => __('Interconnection Request for {0}', h($request->local_tool_connector_name)), + 'size' => 'xl', 'type' => 'custom', 'bodyHtml' => sprintf('
%s
%s
', $progress, diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index a4948aa..c404c0e 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -6,6 +6,7 @@ use App\Controller\AppController; use Cake\Database\Expression\QueryExpression; use Cake\Event\EventInterface; use Cake\ORM\TableRegistry; +use Cake\Utility\Inflector; use Cake\Utility\Hash; use Cake\Utility\Text; use Cake\Http\Exception\NotFoundException; @@ -59,7 +60,7 @@ class InboxController extends AppController public function delete($id) { if ($this->request->is('post')) { - $request = $this->Inbox->get($id); + $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); $processor = $this->requestProcessor->getProcessor($request->scope, $request->action); $discardResult = $processor->discard($id, $request); @@ -77,7 +78,7 @@ class InboxController extends AppController public function process($id) { - $request = $this->Inbox->get($id); + $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); $scope = $request->scope; $action = $request->action; $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); @@ -129,22 +130,33 @@ class InboxController extends AppController ]; $entryData['data'] = $this->request->data ?? []; // $entryData['data'] = [ - // 'toolName' => 'MISP', - // 'url' => 'http://localhost:8000', + // 'connectorName' => 'MispConnector', + // 'cerebrateURL' => 'http://localhost:8000', + // 'url' => 'https://localhost:8443', // 'email' => 'admin@admin.test', // 'authkey' => 'DkM9fEfwrG8Bg3U0ncKamocIutKt5YaUFuxzsB6b', // ]; $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); if ($scope == 'LocalTool') { - if (empty($entryData['data']['toolName']) || empty($entryData['data']['url'])) { - throw new MethodNotAllowedException(__('Could not create entry. Tool name or URL is missing')); + $this->validateLocalToolRequestEntry($entryData); + $entryData['origin'] = $entryData['data']['cerebrateURL']; + $processor = $this->requestProcessor->getLocalToolProcessor($action, $entryData['data']['connectorName']); + $errors = $this->Inbox->checkUserBelongsToBroodOwnerOrg($this->ACL->getUser(), $entryData); + if (!empty($errors)) { + $message = __('Could not create inbox message'); + return $this->RestResponse->ajaxFailResponse(Inflector::singularize($this->Inbox->getAlias()), 'createInboxEntry', [], $message, $errors); } - $entryData['origin'] = $entryData['data']['url']; - $processor = $this->requestProcessor->getLocalToolProcessor($action, $entryData['data']['toolName']); } else { $processor = $this->requestProcessor->getProcessor($scope, $action); } $creationResult = $this->requestProcessor->createInboxEntry($processor, $entryData); return $processor->genHTTPReply($this, $creationResult); } + + private function validateLocalToolRequestEntry($entryData) + { + if (empty($entryData['data']['connectorName']) || empty($entryData['data']['cerebrateURL'])) { + throw new MethodNotAllowedException(__('Could not create entry. Tool name or URL is missing')); + } + } } diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 195c689..b78ae97 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -186,8 +186,9 @@ class LocalToolsController extends AppController }, 'afterFind' => function($data) { foreach ($data as $connector) { - $connectorClass = array_values($this->LocalTools->getConnectorByConnectionId($connector['id']))[0]; - $connector['toolName'] = $connectorClass->name; + $connectorById = $this->LocalTools->getConnectorByConnectionId($connector['id']) + $className = array_keys($connectorById)[0]; + $connector['connectorName'] = $className; } return $data; } @@ -222,18 +223,12 @@ class LocalToolsController extends AppController $this->loadModel('Broods'); $remoteCerebrate = $this->Broods->find()->where(['id' => $params['cerebrate_id']])->first(); if ($this->request->is(['post', 'put'])) { - $postParams = $this->ParamHandler->harvestParams(['local_tool_id', 'tool_name']); + $postParams = $this->ParamHandler->harvestParams(['local_tool_id']); if (empty($postParams['local_tool_id'])) { throw new MethodNotAllowedException(__('No local tool ID supplied.')); } - if (empty($postParams['tool_name'])) { - throw new MethodNotAllowedException(__('No local tool name supplied.')); - } $params['local_tool_id'] = $postParams['local_tool_id']; $encodingResult = $this->LocalTools->encodeConnection($params); - $encodingResult['toolName'] = $tool_name; - $urlPath = '/inbox/createInboxEntry/LocalTool/IncomingConnectionRequest'; - $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $encodingResult); $this->redirect(); } else { $remoteTool = $this->LocalTools->getRemoteToolById($params); diff --git a/src/Model/Entity/Inbox.php b/src/Model/Entity/Inbox.php index c382398..8192d4e 100644 --- a/src/Model/Entity/Inbox.php +++ b/src/Model/Entity/Inbox.php @@ -7,13 +7,13 @@ use Cake\ORM\Entity; class Inbox extends AppModel { - protected $_virtual = ['local_tool_name']; + protected $_virtual = ['local_tool_connector_name']; - protected function _getLocalToolName() + protected function _getLocalToolConnectorName() { $localToolName = null; - if (!empty($this->data) && !empty($this->data['toolName'])) { - $localToolName = $this->data['toolName']; + if (!empty($this->data) && !empty($this->data['connectorName'])) { + $localToolName = $this->data['connectorName']; } return $localToolName; } diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php index ca4664f..5b0d8bf 100644 --- a/src/Model/Table/InboxTable.php +++ b/src/Model/Table/InboxTable.php @@ -7,6 +7,7 @@ use Cake\Database\Type; use Cake\ORM\Table; use Cake\ORM\RulesChecker; use Cake\Validation\Validator; +use Cake\Http\Exception\NotFoundException; Type::map('json', 'Cake\Database\Type\JsonType'); @@ -44,7 +45,7 @@ class InboxTable extends AppTable ->notEmptyString('title') ->notEmptyString('origin') ->datetime('created') - + ->requirePresence([ 'scope' => ['message' => __('The field `scope` is required')], 'action' => ['message' => __('The field `action` is required')], @@ -85,4 +86,27 @@ class InboxTable extends AppTable throw new NotFoundException(__('Could not post to the requested resource.')); } } + + public function checkUserBelongsToBroodOwnerOrg($user, $entryData) { + $this->Broods = \Cake\ORM\TableRegistry::getTableLocator()->get('Broods'); + $this->Individuals = \Cake\ORM\TableRegistry::getTableLocator()->get('Individuals'); + $errors = []; + $brood = $this->Broods->find() + ->where(['url' => $entryData['origin']]) + ->first(); + if (empty($brood)) { + $errors[] = __('Unkown brood `{0}`', $entryData['data']['cerebrateURL']); + } + + $found = false; + foreach ($user->individual->organisations as $organisations) { + if ($organisations->id == $brood->organisation_id) { + $found = true; + } + } + if (!$found) { + $errors[] = __('User is not part of the brood organisation'); + } + return $errors; + } } diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 25d124e..ea701d5 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -206,6 +206,7 @@ class LocalToolsTable extends AppTable { $params = $this->buildConnectionParams($params); $result = $params['connector'][$params['remote_tool']['connector']]->initiateConnectionWrapper($params); + $this->sendEncodedConnection($params['remote_cerebrate'], $params['remote_tool']['connector'], $result); return $result; } @@ -242,4 +243,13 @@ class LocalToolsTable extends AppTable } return $local_tools; } + + public function sendEncodedConnection($remoteCerebrate, $connectorName, $encodedConnection) + { + $encodedConnection['connectorName'] = $connectorName; + $encodedConnection['cerebrateURL'] = Configure::read('App.fullBaseUrl'); + $urlPath = '/inbox/createInboxEntry/LocalTool/IncomingConnectionRequest'; + $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $encodedConnection); + // If sending failed: Modify state + add entry in outbox + } } diff --git a/src/Model/Table/RequestProcessorTable.php b/src/Model/Table/RequestProcessorTable.php index d36e922..e18433d 100644 --- a/src/Model/Table/RequestProcessorTable.php +++ b/src/Model/Table/RequestProcessorTable.php @@ -52,10 +52,10 @@ class RequestProcessorTable extends AppTable throw new MissingRequestProcessorException(__('Processor not found')); } - public function getLocalToolProcessor($action, $toolName) + public function getLocalToolProcessor($action, $connectorName) { $scope = "LocalTool"; - $specificScope = "{$toolName}LocalTool"; + $specificScope = "{$connectorName}LocalTool"; try { // try to get specific processor for module name or fall back to generic local tool processor $processor = $this->getProcessor($specificScope, $action); } catch (MissingRequestProcessorException $e) {