diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 44d9f4e..61f6e92 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -205,10 +205,45 @@ class LocalToolsController extends AppController { $this->loadModel('Broods'); $tools = $this->Broods->queryLocalTools($id); + foreach ($tools as $k => $tool) { + $tools[$k]['local_tools'] = $this->LocalTools->appendLocalToolConnections($id, $tool); + } if ($this->ParamHandler->isRest()) { return $this->RestResponse->viewData($tools, 'json'); } + $this->set('id', $id); $this->set('data', $tools); $this->set('metaGroup', 'Administration'); } + + public function connectionRequest($cerebrate_id, $remote_tool_id) + { + $params = [ + 'cerebrate_id' => $cerebrate_id, + 'remote_tool_id' => $remote_tool_id + ]; + if ($this->request->is(['post', 'put'])) { + $postParams = $this->ParamHandler->harvestParams(['local_tool_id']); + if (empty($postParams['local_tool_id'])) { + throw new MethodNotAllowedException(__('No local tool ID supplied.')); + } + $params['local_tool_id'] = $postParams['local_tool_id']; + $result = $this->LocalTools->encodeConnection($params); + // Send message to remote inbox + debug($result); + } else { + $this->loadModel('Broods'); + $remoteCerebrate = $this->Broods->find()->where(['id' => $params['cerebrate_id']])->first(); + $remoteTool = $this->LocalTools->getRemoteToolById($params); + $local_tools = $this->LocalTools->encodeConnectionChoice($params); + if (empty($local_tools)) { + throw new NotFoundException(__('No local equivalent tool found.')); + } + $this->set('data', [ + 'remoteCerebrate' => $remoteCerebrate, + 'remoteTool' => $remoteTool, + 'local_tools' => $local_tools + ]); + } + } } diff --git a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php index c4ab2ef..acfc2e3 100644 --- a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php +++ b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php @@ -12,6 +12,10 @@ class CommonConnectorTools ]; public $version = '???'; + const STATE_INITIAL = 'Request issued'; + const STATE_ACCEPT = 'Request accepted'; + const STATE_CONNECTED = 'Connected'; + public function addExposedFunction(string $functionName): void { $this->exposedFunctions[] = $functionName; @@ -48,6 +52,57 @@ class CommonConnectorTools $sharing_groups->captureSharingGroup($input); return true; } + + public function remoteToolConnectionStatus(array $params, string $status): void + { + $remoteToolConnections = \Cake\ORM\TableRegistry::getTableLocator()->get('RemoteToolConnections'); + $remoteToolConnection = $remoteToolConnections->find()->where( + [ + 'local_tool_id' => $params['connection']['id'], + 'remote_tool_id' => $params['remote_tool']['id'], + 'brood_id' => $params['remote_cerebrate']['id'] + ] + )->first(); + if (empty($remoteToolConnection)) { + $data = $remoteToolConnections->newEmptyEntity(); + $entry = [ + 'local_tool_id' => $params['connection']['id'], + 'remote_tool_id' => $params['remote_tool']['id'], + 'remote_tool_name' => $params['remote_tool']['name'], + 'brood_id' => $params['remote_cerebrate']['id'], + 'name' => '', + 'settings' => '', + 'status' => $status, + 'created' => time(), + 'modified' => time() + ]; + $data = $remoteToolConnections->patchEntity($data, $entry); + $remoteToolConnections->save($data); + } else { + $data = $remoteToolConnections->patchEntity($remoteToolConnection, ['status' => $status, 'modified' => time()]); + $remoteToolConnections->save($data); + } + } + + public function initiateConnectionWrapper(array $params): array + { + $result = $this->initiateConnection($params); + $this->remoteToolConnectionStatus($params, self::STATE_INITIAL); + return $result; + } + + public function acceptConnectionWrapper(array $params): array + { + $result = $this->acceptConnection($params); + $this->remoteToolConnectionStatus($params, self::STATE_ACCEPT); + return $result; + } + + public function finaliseConnectionWrapper(array $params): bool + { + $this->remoteToolConnectionStatus($params, self::STATE_CONNECTED); + return false; + } } ?> diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 1acd685..d86afad 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -143,6 +143,9 @@ class MispConnector extends CommonConnectorTools if ($response->isOk()) { return $response; } else { + if (!empty($params['softError'])) { + return $response; + } throw new NotFoundException(__('Could not retrieve the requested resource.')); } } @@ -537,11 +540,128 @@ class MispConnector extends CommonConnectorTools if ($response->getStatusCode() == 200) { return ['success' => 1, 'message' => __('Setting saved.')]; } else { - return ['success' => 0, 'message' => __('Could not fetch the remote sharing group.')]; + return ['success' => 0, 'message' => __('Could not update.')]; } } throw new MethodNotAllowedException(__('Invalid http request type for the given action.')); + } + public function initiateConnection(array $params): array + { + $params['connection_settings'] = json_decode($params['connection']['settings'], true); + $params['misp_organisation'] = $this->getSetOrg($params); + $params['sync_user'] = $this->createSyncUser($params); + return [ + 'email' => $params['sync_user']['email'], + 'authkey' => $params['sync_user']['authkey'], + 'url' => $params['connection_settings']['url'] + ]; + } + + public function acceptConnection(array $params): array + { + $params['sync_user_enabled'] = true; + $params['connection_settings'] = json_decode($params['connection']['settings'], true); + $params['misp_organisation'] = $this->getSetOrg($params); + $params['sync_user'] = $this->createSyncUser($params); + $params['sync_connection'] = $this->addServer([ + 'authkey' => $params['remote_tool']['authkey'], + 'url' => $params['remote_tool']['url'], + 'name' => $params['remote_tool']['name'], + 'remote_org_id' => $params['misp_organisation']['id'] + ]); + return [ + 'email' => $params['sync_user']['email'], + 'authkey' => $params['sync_user']['authkey'], + 'url' => $params['connection_settings']['url'] + ]; + } + + public function finaliseConnection(array $params): bool + { + $params['sync_connection'] = $this->addServer([ + 'authkey' => $params['remote_tool']['authkey'], + 'url' => $params['remote_tool']['url'], + 'name' => $params['remote_tool']['name'], + 'remote_org_id' => $params['misp_organisation']['id'] + ]); + return true; + } + + private function getSetOrg(array $params): array + { + $params['softError'] = 1; + $response = $this->getData('/organisations/view/' . $params['remote_org']['uuid'], $params); + if ($response->isOk()) { + $organisation = $response->getJson()['Organisation']; + if (!$organisation['local']) { + $organisation['local'] = 1; + $response = $this->postData('/admin/organisations/edit/' . $organisation['id'], $params); + if (!$response->isOk()) { + throw new MethodNotAllowedException(__('Could not update the organisation in MISP.')); + } + } + } else { + $params['body'] = [ + 'uuid' => $params['remote_org']['uuid'], + 'name' => $params['remote_org']['name'], + 'local' => 1 + ]; + $response = $this->postData('/admin/organisations/add', $params); + if ($response->isOk()) { + $organisation = $response->getJson()['Organisation']; + } else { + throw new MethodNotAllowedException(__('Could not create the organisation in MISP.')); + } + } + return $organisation; + } + + private function createSyncUser(array $params): array + { + $params['softError'] = 1; + $user = [ + 'email' => 'sync_%s@' . parse_url($params['remote_cerebrate']['url'])['host'], + 'org_id' => $params['misp_organisation']['id'], + 'role_id' => empty($params['connection_settings']['role_id']) ? 5 : $params['connection_settings']['role_id'], + 'disabled' => 1, + 'change_pw' => 0, + 'termsaccepted' => 1 + ]; + return $this->createUser($user, $params); + } + + private function addServer(array $params): array + { + if ( + empty($params['authkey']) || + empty($params['url']) || + empty($params['remote_org_id']) || + empty($params['name']) + ) { + throw new MethodNotAllowedException(__('Required data missing from the sync connection object. The following fields are required: [name, url, authkey, org_id].')); + } + $response = $this->postData('/servers/add', $params); + if (!$response->isOk()) { + throw new MethodNotAllowedException(__('Could not add Server in MISP.')); + } + return $response->getJson()['Server']; + } + + private function createUser(array $user, array $params): array + { + if (strpos($user['email'], '%s') !== false) { + $user['email'] = sprintf( + $user['email'], + \Cake\Utility\Security::randomString(8) + ); + } + $params['body'] = $user; + $response = $this->postData('/admin/users/add', $params); + if (!$response->isOk()) { + throw new MethodNotAllowedException(__('Could not add the user in MISP.')); + } + return $response->getJson()['User']; } } diff --git a/src/Model/Entity/RemoteToolConnection.php b/src/Model/Entity/RemoteToolConnection.php new file mode 100644 index 0000000..164ddd8 --- /dev/null +++ b/src/Model/Entity/RemoteToolConnection.php @@ -0,0 +1,11 @@ +get('Broods'); + $tools = $broods->queryLocalTools($params['cerebrate_id']); + $remoteTool = []; + foreach ($tools as $tool) { + if ($tool['id'] === intval($params['remote_tool_id'])) { + $remoteTool = $tool; + } + } + if (empty($remoteTool)) { + throw new NotFoundException(__('Invalid remote tool specified.')); + } + return $remoteTool; + } + + public function encodeConnectionChoice(array $params): array + { + $remoteTool = $this->getRemoteToolById($params); + $connections = $this->find()->where(['connector' => $remoteTool['connector']])->toArray(); + $results = []; + foreach ($connections as $connection) { + $results[] = [ + 'id' => $connection->id, + 'name' => $connection->name + ]; + } + return $results; + } + + public function encodeConnection(array $params): array + { + $params = $this->buildConnectionParams($params); + $result = $params['connector'][$params['remote_tool']['connector']]->initiateConnectionWrapper($params); + return $result; + } + + public function buildConnectionParams(array $params): array + { + $remote_tool = $this->getRemoteToolById($params); + $broods = \Cake\ORM\TableRegistry::getTableLocator()->get('Broods'); + $remote_cerebrate = $broods->find()->where(['id' => $params['cerebrate_id']])->first(); + $connector = $this->getConnectors($remote_tool['connector']); + $connection = $this->find()->where(['id' => $params['local_tool_id']])->first(); + $remote_org = $broods->Organisations->find()->where(['id' => $remote_cerebrate['organisation_id']])->first(); + if (empty($connector[$remote_tool['connector']])) { + throw new NotFoundException(__('No valid connector found for the remote tool.')); + } + return [ + 'remote_cerebrate' => $remote_cerebrate, + 'remote_org' => $remote_org, + 'remote_tool' => $remote_tool, + 'connector' => $connector, + 'connection' => $connection, + //'message' => + ]; + } + + public function appendLocalToolConnections(int $brood_id, array $tool): array + { + $remoteToolConnections = \Cake\ORM\TableRegistry::getTableLocator()->get('RemoteToolConnections'); + $connections = $remoteToolConnections->find()->where(['remote_tool_id' => $tool['id'], 'brood_id' => $brood_id])->toArray(); + $local_tools = []; + foreach ($connections as $k => $connection) { + $temp = $this->find()->where(['id' => $connection['local_tool_id']])->select(['id', 'name'])->enableHydration(false)->first(); + $temp['status'] = $connection['status']; + $local_tools[] = $temp; + } + return $local_tools; + } } diff --git a/src/Model/Table/RemoteToolConnectionsTable.php b/src/Model/Table/RemoteToolConnectionsTable.php new file mode 100644 index 0000000..7e8cf21 --- /dev/null +++ b/src/Model/Table/RemoteToolConnectionsTable.php @@ -0,0 +1,27 @@ +BelongsTo( + 'LocalTools' + ); + $this->setDisplayField('id'); + } + + public function validationDefault(Validator $validator): Validator + { + return $validator; + } +} diff --git a/templates/LocalTools/brood_tools.php b/templates/LocalTools/brood_tools.php index ae41159..d069eca 100644 --- a/templates/LocalTools/brood_tools.php +++ b/templates/LocalTools/brood_tools.php @@ -29,6 +29,11 @@ echo $this->element('genericElements/IndexTable/index_table', [ [ 'name' => __('Description'), 'data_path' => 'description', + ], + [ + 'name' => __('Connected Local Tools'), + 'data_path' => 'local_tool', + 'element' => 'local_tools_status' ] ], 'title' => __('Local tools made available by the remote Cerebrate'), @@ -37,8 +42,8 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'skip_pagination' => 1, 'actions' => [ [ - 'url' => '/localTools/connectionRequest', - 'url_params_data_paths' => ['id'], + 'open_modal' => sprintf('/localTools/connectionRequest/%s/[onclick_params_data_path]', h($id)), + 'modal_params_data_path' => 'id', 'title' => 'Issue a connection request', 'icon' => 'plug' ] diff --git a/templates/LocalTools/connection_request.php b/templates/LocalTools/connection_request.php new file mode 100644 index 0000000..8f672e7 --- /dev/null +++ b/templates/LocalTools/connection_request.php @@ -0,0 +1,27 @@ +element('genericElements/Form/genericForm', [ + 'data' => [ + 'description' => __( + 'Connect the remote tool ({0}) on remote brood ({1}) using the local tool selected below.', + h($data['remoteTool']['name']), + h($data['remoteCerebrate']['name']) + ), + 'model' => 'LocalTools', + 'fields' => [ + [ + 'field' => 'local_tool_id', + 'options' => $dropdown, + 'type' => 'dropdown' + ] + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] + ]); +?> + diff --git a/templates/element/genericElements/IndexTable/Fields/local_tools_status.php b/templates/element/genericElements/IndexTable/Fields/local_tools_status.php new file mode 100644 index 0000000..118b915 --- /dev/null +++ b/templates/element/genericElements/IndexTable/Fields/local_tools_status.php @@ -0,0 +1,13 @@ +Hash->extract($row, 'local_tools'); + $output = []; + foreach ($tools as $tool) { + $output[] = sprintf( + '%s: %s', + h($tool['id']), + h($tool['name']), + h($tool['status']) + ); + } + echo implode('
', $output); +?>