From 551ca0d83fc2c209b769cc3b0a23ad3c60a659b6 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 11 Jun 2021 10:44:24 +0200 Subject: [PATCH 01/78] chg: [wip] localtools --- .../CommonConnectorTools.php | 54 ++++++++++- .../local_tool_connectors/MispConnector.php | 94 +++++++++++++------ src/Model/Entity/RemoteToolConnection.php | 11 +++ src/Model/Table/LocalToolsTable.php | 15 ++- .../Table/RemoteToolConnectionsTable.php | 27 ++++++ templates/LocalTools/brood_tools.php | 4 +- 6 files changed, 170 insertions(+), 35 deletions(-) create mode 100644 src/Model/Entity/RemoteToolConnection.php create mode 100644 src/Model/Table/RemoteToolConnectionsTable.php diff --git a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php index 13184c5..b4924ee 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; @@ -49,11 +53,57 @@ class CommonConnectorTools return true; } - public function encodeConnection(array $params): array + public function remoteToolConnectionStatus(array $params, string $status): void { - $result = $this->encodeConnection($params); + $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() + ]; + debug($entry); + $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 be202e5..5a7467a 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -540,18 +540,46 @@ class MispConnector extends CommonConnectorTools if ($response->getStatusCode() == 200) { return ['success' => 1, 'message' => __('Setting saved.')]; } else { - return ['success' => 0, 'message' => __('Could not save the setting.')]; + return ['success' => 0, 'message' => __('Could not update.')]; } } throw new MethodNotAllowedException(__('Invalid http request type for the given action.')); } - public function encodeConnectionAction(array $params): array + public function initiateConnection(array $params): array { - if (empty($params['org_uuid'])) { - throw new MethodNotAllowedException(__('No org uuid passed, cannot encode connection.')); - } - return []; + $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 + { + return true; } private function getSetOrg(array $params): array @@ -586,36 +614,48 @@ class MispConnector extends CommonConnectorTools private function createSyncUser(array $params): array { $params['softError'] = 1; - $username = sprintf( - 'sync_%s@%s', - \Cake\Utility\Security::randomString(8), - parse_url($params['remote_cerebrate']['url'])['host'] - ); - $params['body'] = [ - 'email' => $username, + $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 ]; - $response = $this->postData('/admin/users/add', $params); - if (!$response->isOk()) { - throw new MethodNotAllowedException(__('Could not update the organisation in MISP.')); - } - return $response->getJson()['User']; + return $this->createUser($user, $params); } - public function connectToRemoteTool(array $params): array + private function addServer(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'] - ]; + 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 @@ +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'); @@ -207,13 +214,13 @@ class LocalToolsTable extends AppTable if (empty($connector[$remote_tool['connector']])) { throw new NotFoundException(__('No valid connector found for the remote tool.')); } - $result = $connector[$remote_tool['connector']]->connectToRemoteTool([ + return [ 'remote_cerebrate' => $remote_cerebrate, 'remote_org' => $remote_org, 'remote_tool' => $remote_tool, 'connector' => $connector, - 'connection' => $connection - ]); - return $result; + 'connection' => $connection, + //'message' => + ]; } } 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..9b73ae9 100644 --- a/templates/LocalTools/brood_tools.php +++ b/templates/LocalTools/brood_tools.php @@ -37,8 +37,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' ] From 2e9b306f46ac66cac43fe9b94bf8f3183c23b4eb Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 11 Jun 2021 14:27:22 +0200 Subject: [PATCH 02/78] chg: [wip] local tools integration --- src/Controller/LocalToolsController.php | 3 +++ .../local_tool_connectors/CommonConnectorTools.php | 1 - .../default/local_tool_connectors/MispConnector.php | 6 ++++++ src/Model/Table/LocalToolsTable.php | 13 +++++++++++++ templates/LocalTools/brood_tools.php | 5 +++++ .../IndexTable/Fields/local_tools_status.php | 13 +++++++++++++ 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 templates/element/genericElements/IndexTable/Fields/local_tools_status.php diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 9d4a20c..61f6e92 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -205,6 +205,9 @@ 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'); } diff --git a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php index b4924ee..acfc2e3 100644 --- a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php +++ b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php @@ -76,7 +76,6 @@ class CommonConnectorTools 'created' => time(), 'modified' => time() ]; - debug($entry); $data = $remoteToolConnections->patchEntity($data, $entry); $remoteToolConnections->save($data); } else { diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 5a7467a..d86afad 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -579,6 +579,12 @@ class MispConnector extends CommonConnectorTools 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; } diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index ee6e1ac..9cfc48f 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -223,4 +223,17 @@ class LocalToolsTable extends AppTable //'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/templates/LocalTools/brood_tools.php b/templates/LocalTools/brood_tools.php index 9b73ae9..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'), 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..0d6e0b1 --- /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); +?> From 114e4e76e5fa8c4ecbc58b1ff553fc90ed4974c2 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 11 Jun 2021 14:42:08 +0200 Subject: [PATCH 03/78] chg: [no-wrap] connections --- .../genericElements/IndexTable/Fields/local_tools_status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/element/genericElements/IndexTable/Fields/local_tools_status.php b/templates/element/genericElements/IndexTable/Fields/local_tools_status.php index 0d6e0b1..118b915 100644 --- a/templates/element/genericElements/IndexTable/Fields/local_tools_status.php +++ b/templates/element/genericElements/IndexTable/Fields/local_tools_status.php @@ -3,7 +3,7 @@ $output = []; foreach ($tools as $tool) { $output[] = sprintf( - '%s: %s', + '%s: %s', h($tool['id']), h($tool['name']), h($tool['status']) From d125b3b97410611236f1ef3e7f0772d668fdfa49 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 11:57:49 +0200 Subject: [PATCH 04/78] new: [helpers] Added collapse, progress and progressTimeline component + various improvements --- src/View/Helper/BootstrapHelper.php | 275 ++++++++++++++++++++++++++- webroot/css/bootstrap-additional.css | 22 +++ webroot/js/bootstrap-helper.js | 6 + 3 files changed, 300 insertions(+), 3 deletions(-) diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index b63e5f6..0a93bc0 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -42,12 +42,15 @@ namespace App\View\Helper; use Cake\View\Helper; +use Cake\Utility\Hash; use Cake\Utility\Inflector; use Cake\Utility\Security; use InvalidArgumentException; class BootstrapHelper extends Helper { + public $helpers = ['FontAwesome']; + public function tabs($options) { $bsTabs = new BootstrapTabs($options); @@ -80,14 +83,44 @@ class BootstrapHelper extends Helper public function modal($options) { - $bsButton = new BoostrapModal($options); - return $bsButton->modal(); + $bsModal = new BoostrapModal($options); + return $bsModal->modal(); + } + + public function progress($options) + { + $bsProgress = new BoostrapProgress($options); + return $bsProgress->progress(); + } + + public function collapse($options, $content) + { + $bsCollapse = new BoostrapCollapse($options, $content, $this); + return $bsCollapse->collapse(); + } + + public function progressTimeline($options) + { + $bsProgressTimeline = new BoostrapProgressTimeline($options, $this); + return $bsProgressTimeline->progressTimeline(); } } class BootstrapGeneric { public static $variants = ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark', 'white', 'transparent']; + public static $textClassByVariants = [ + 'primary' => 'text-white', + 'secondary' => 'text-white', + 'success' => 'text-white', + 'danger' => 'text-white', + 'warning' => 'text-black', + 'info' => 'text-white', + 'light' => 'text-black', + 'dark' => 'text-white', + 'white' => 'text-black', + 'transparent' => 'text-black' + ]; protected $allowedOptionValues = []; protected $options = []; @@ -146,6 +179,11 @@ class BootstrapGeneric 'arial-hidden' => 'true' ], '×')); } + + protected static function getTextClassForVariant($variant) + { + return !empty(self::$textClassByVariants[$variant]) ? self::$textClassByVariants[$variant] : 'text-black'; + } } class BootstrapTabs extends BootstrapGeneric @@ -543,7 +581,7 @@ class BoostrapTable extends BootstrapGeneric { } else { $key = $field; } - $cellValue = $row[$key]; + $cellValue = Hash::get($row, $key); $html .= $this->genCell($cellValue, $field, $row); } } else { // indexed array @@ -856,3 +894,234 @@ class BoostrapModal extends BootstrapGeneric { return $buttonCancel . $buttonConfirm; } } + +class BoostrapProgress extends BootstrapGeneric { + private $defaultOptions = [ + 'value' => 0, + 'total' => 100, + 'text' => '', + 'title' => '', + 'variant' => 'primary', + 'height' => '', + 'striped' => false, + 'animated' => false, + 'label' => true + ]; + + function __construct($options) { + $this->allowedOptionValues = [ + 'variant' => BootstrapGeneric::$variants, + ]; + $this->processOptions($options); + } + + private function processOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + $this->checkOptionValidity(); + } + + public function progress() + { + return $this->genProgress(); + } + + private function genProgress() + { + $percentage = round(100 * $this->options['value'] / $this->options['total']); + $heightStyle = !empty($this->options['height']) ? sprintf('height: %s;', h($this->options['height'])) : ''; + $widthStyle = sprintf('width: %s%%;', $percentage); + $label = $this->options['label'] ? "{$percentage}%" : ''; + $pb = $this->genNode('div', [ + 'class' => [ + 'progress-bar', + "bg-{$this->options['variant']}", + $this->options['striped'] ? 'progress-bar-striped' : '', + $this->options['animated'] ? 'progress-bar-animated' : '', + ], + 'role' => "progressbar", + 'aria-valuemin' => "0", 'aria-valuemax' => "100",'aria-valuenow' => $percentage, + 'style' => "${widthStyle}", + 'title' => $this->options['title'] + ], $label); + $container = $this->genNode('div', [ + 'class' => [ + 'progress', + ], + 'style' => "${heightStyle}", + 'title' => h($this->options['title']), + ], $pb); + return $container; + } +} + +class BoostrapCollapse extends BootstrapGeneric { + private $defaultOptions = [ + 'text' => '', + 'open' => false, + ]; + + function __construct($options, $content, $btHelper) { + $this->allowedOptionValues = []; + $this->processOptions($options); + $this->content = $content; + $this->btHelper = $btHelper; + } + + private function processOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + $this->checkOptionValidity(); + } + + public function collapse() + { + return $this->genCollapse(); + } + + private function genControl() + { + $html = $this->genNode('a', [ + 'class' => ['text-decoration-none'], + 'data-toggle' => 'collapse', + 'href' => '#collapseExample', + 'role' => 'button', + 'aria-expanded' => 'false', + 'aria-controls' => 'collapseExample', + ], h($this->options['title'])); + return $html; + } + + private function genContent() + { + $content = $this->genNode('div', [ + 'class' => 'card', + ], $this->content); + $container = $this->genNode('div', [ + 'class' => ['collapse', $this->options['open'] ? 'show' : ''], + 'id' => 'collapseExample', + ], $content); + return $container; + } + + private function genCollapse() + { + $html = $this->genControl(); + $html .= $this->genContent(); + return $html; + } +} + +class BoostrapProgressTimeline extends BootstrapGeneric { + private $defaultOptions = [ + 'steps' => [], + 'selected' => 0, + 'variant' => 'info', + 'variantInactive' => 'secondary', + ]; + + function __construct($options, $btHelper) { + $this->allowedOptionValues = [ + 'variant' => BootstrapGeneric::$variants, + 'variantInactive' => BootstrapGeneric::$variants, + ]; + $this->processOptions($options); + $this->btHelper = $btHelper; + } + + private function processOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + $this->checkOptionValidity(); + } + + public function progressTimeline() + { + return $this->genProgressTimeline(); + } + + private function getStepIcon($step, $i, $nodeActive, $lineActive) + { + $icon = $this->genNode('b', [ + 'class' => [ + !empty($step['icon']) ? h($this->btHelper->FontAwesome->getClass($step['icon'])) : '', + $this->getTextClassForVariant($this->options['variant']) + ], + ], empty($step['icon']) ? h($i+1) : ''); + $iconContainer = $this->genNode('span', [ + 'class' => [ + 'd-flex', 'align-items-center', 'justify-content-center', + 'rounded-circle', + $nodeActive ? "bg-{$this->options['variant']}" : "bg-{$this->options['variantInactive']}" + ], + 'style' => 'width:50px; height:50px' + ], $icon); + $li = $this->genNode('li', [ + 'class' => [ + 'd-flex', 'flex-column', + $nodeActive ? 'progress-active' : 'progress-inactive', + ], + ], $iconContainer); + $html = $li . $this->getHorizontalLine($i, $nodeActive, $lineActive); + return $html; + } + + private function getHorizontalLine($i, $nodeActive, $lineActive) + { + $stepCount = count($this->options['steps']); + if ($i == $stepCount-1) { + return ''; + } + $progressBar = (new BoostrapProgress([ + 'label' => false, + 'value' => $nodeActive ? ($lineActive ? 100 : 50) : 0, + 'height' => '2px', + 'variant' => $this->options['variant'] + ]))->progress(); + $line = $this->genNode('span', [ + 'class' => [ + 'progress-line', + 'flex-grow-1', 'align-self-center', + $lineActive ? "bg-{$this->options['variant']}" : '' + ], + ], $progressBar); + return $line; + } + + private function getStepText($step, $isActive) + { + return $this->genNode('li', [ + 'class' => [ + 'text-center', + 'font-weight-bold', + $isActive ? 'progress-active' : 'progress-inactive', + ], + ], h($step['text'] ?? '')); + } + + private function genProgressTimeline() + { + $iconLis = ''; + $textLis = ''; + foreach ($this->options['steps'] as $i => $step) { + $nodeActive = $i <= $this->options['selected']; + $lineActive = $i < $this->options['selected']; + $iconLis .= $this->getStepIcon($step, $i, $nodeActive, $lineActive); + $textLis .= $this->getStepText($step, $nodeActive); + } + $ulIcons = $this->genNode('ul', [ + 'class' => [ + 'd-flex', 'justify-content-around', + ], + ], $iconLis); + $ulText = $this->genNode('ul', [ + 'class' => [ + 'd-flex', 'justify-content-between', + ], + ], $textLis); + $html = $this->genNode('div', [ + 'class' => ['progress-timeline'] + ], $ulIcons . $ulText); + return $html; + } +} \ No newline at end of file diff --git a/webroot/css/bootstrap-additional.css b/webroot/css/bootstrap-additional.css index 981324c..e25fd7c 100644 --- a/webroot/css/bootstrap-additional.css +++ b/webroot/css/bootstrap-additional.css @@ -66,3 +66,25 @@ .toast-dark strong { color: #040505; } + +div.progress-timeline { + padding: 0.2em 0.2em 0.5em 0.2em; +} +div.progress-timeline ul { + position: relative; + padding: 0; +} +div.progress-timeline li { + list-style-type: none; + position: relative +} +div.progress-timeline li.progress-inactive { + opacity: 0.5; +} +div.progress-timeline .progress-line { + height: 2px; + /* background: gray; */ +} +div.progress-timeline .progress-line.progress-inactive { + opacity: 0.5; +} \ No newline at end of file diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index 8cb39e3..d345beb 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -42,6 +42,12 @@ class UIFactory { theModal.show() theModal.$modal.data('modalObject', theModal) return theModal + }).catch((error) => { + UI.toast({ + variant: 'danger', + title: 'Error while loading the processor', + body: error.message + }) }) } From 7e47c48ef395d8e17c2110b660e2f8adce06be0f Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 11:59:40 +0200 Subject: [PATCH 05/78] chg: [genericTemplate:index_simple] Moved file to element --- .../genericElements}/index_simple.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/{genericTemplates => element/genericElements}/index_simple.php (100%) diff --git a/templates/genericTemplates/index_simple.php b/templates/element/genericElements/index_simple.php similarity index 100% rename from templates/genericTemplates/index_simple.php rename to templates/element/genericElements/index_simple.php From 835dbd88c8a6ef5055e11742f2179f7f646dbbf8 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:00:06 +0200 Subject: [PATCH 06/78] fix: [genericElement:singleView] Allow displaying object not included in an array --- .../element/genericElements/SingleViews/Fields/jsonField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/element/genericElements/SingleViews/Fields/jsonField.php b/templates/element/genericElements/SingleViews/Fields/jsonField.php index 9c8b16b..52c0c32 100644 --- a/templates/element/genericElements/SingleViews/Fields/jsonField.php +++ b/templates/element/genericElements/SingleViews/Fields/jsonField.php @@ -4,7 +4,7 @@ $string = $field['raw']; } else { $value = Cake\Utility\Hash::extract($data, $field['path']); - $string = empty($value[0]) ? '' : $value[0]; + $string = count($value) == 0 ? '' : $value; } echo sprintf( '
', From edc23d8b71ca400053ccbd0f1eab755e90da889e Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:01:03 +0200 Subject: [PATCH 07/78] fix: [genericElements:indexTable] Enforce actions button to be inline --- templates/element/genericElements/IndexTable/Fields/actions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/element/genericElements/IndexTable/Fields/actions.php b/templates/element/genericElements/IndexTable/Fields/actions.php index e0d275b..d399c53 100644 --- a/templates/element/genericElements/IndexTable/Fields/actions.php +++ b/templates/element/genericElements/IndexTable/Fields/actions.php @@ -17,7 +17,7 @@ * - function($row, $options): the lambda function. $row contain the row data * - options: array of options. datapaths described in the datapath keyname will be extracted and replaced with the actual row value */ - echo ''; + echo ''; foreach ($actions as $action) { if (isset($action['requirement']) && !$action['requirement']) { continue; From 543603339b645c1f750fd06d3e096c5026d79c7a Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:04:11 +0200 Subject: [PATCH 08/78] new: [inbox] Added list request processors --- src/Controller/Component/ACLComponent.php | 5 +++ src/Controller/InboxController.php | 22 ++------- templates/Inbox/list_processors.php | 54 +++++++++++++++++++++++ 3 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 templates/Inbox/list_processors.php diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index eec94ce..55c2161 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -662,6 +662,11 @@ class ACLComponent extends Component 'actions' => ['delete', 'edit', 'view'], 'skipTopMenu' => 1, 'popup' => 1 + ], + 'listProcessors' => [ + 'url' => '/inbox/listProcessors', + 'label' => __('List Request Processors'), + 'skipTopMenu' => 1 ] ] ], diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 6c0f978..8997529 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -105,27 +105,13 @@ class InboxController extends AppController $data[] = [ 'enabled' => $processor->enabled, 'scope' => $scope, - 'action' => $processor->action + 'action' => $processor->action, + 'description' => isset($processor->getDescription) ? $processor->getDescription() : null, + 'notice' => $processor->notice ?? null, + 'error' => $processor->error ?? null, ]; } } - $this->set('title', 'Available request processors'); - $this->set('fields', [ - [ - 'name' => 'Enabled', - 'data_path' => 'enabled', - 'element' => 'boolean' - ], - [ - 'name' => 'Processor scope', - 'data_path' => 'scope', - ], - [ - 'name' => 'Processor action', - 'data_path' => 'action', - ] - ]); $this->set('data', $data); - $this->render('/genericTemplates/index_simple'); } } diff --git a/templates/Inbox/list_processors.php b/templates/Inbox/list_processors.php new file mode 100644 index 0000000..fd93199 --- /dev/null +++ b/templates/Inbox/list_processors.php @@ -0,0 +1,54 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'skip_pagination' => true, + 'data' => $data, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'context_filters', + 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] + ], + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'allowFilering' => true + ] + ] + ], + 'fields' => [ + [ + 'name' => __('Enabled'), + 'data_path' => 'enabled', + 'element' => 'boolean' + ], + [ + 'name' => __('Processor scope'), + 'data_path' => 'scope', + ], + [ + 'name' => __('Processor action'), + 'data_path' => 'action', + ], + [ + 'name' => __('Description'), + 'data_path' => 'description', + ], + [ + 'name' => __('Notice'), + 'data_path' => 'notice', + ], + [ + 'name' => __('Error'), + 'data_path' => 'error', + ], + ], + 'title' => __('Available Inbox Request Processors'), + 'description' => __('The list of Inbox Request Processors available on this server.'), + 'actions' => [ + ] + ] +]); \ No newline at end of file From d76aa04ad7166ce76c558dbd64712f8e29884905 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:04:57 +0200 Subject: [PATCH 09/78] chg: [genericElement:form] More flexible variable check --- templates/element/genericElements/Form/genericForm.php | 2 +- templates/element/genericElements/Form/submitButton.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/element/genericElements/Form/genericForm.php b/templates/element/genericElements/Form/genericForm.php index 9e3ba2e..5807da6 100644 --- a/templates/element/genericElements/Form/genericForm.php +++ b/templates/element/genericElements/Form/genericForm.php @@ -83,7 +83,7 @@ $submitButtonData['ajaxSubmit'] = $ajaxSubmit; } $ajaxFlashMessage = ''; - if ($ajax) { + if (!empty($ajax)) { $ajaxFlashMessage = sprintf( '
%s
', $this->Flash->render() diff --git a/templates/element/genericElements/Form/submitButton.php b/templates/element/genericElements/Form/submitButton.php index 134e857..55062dd 100644 --- a/templates/element/genericElements/Form/submitButton.php +++ b/templates/element/genericElements/Form/submitButton.php @@ -1,5 +1,5 @@ Date: Sat, 12 Jun 2021 12:05:57 +0200 Subject: [PATCH 10/78] chg: [requestProcessor:brood] Moved localTool to its own processor file --- .../BroodRequestProcessor.php | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/libraries/default/RequestProcessors/BroodRequestProcessor.php b/libraries/default/RequestProcessors/BroodRequestProcessor.php index 13a0c33..b598705 100644 --- a/libraries/default/RequestProcessors/BroodRequestProcessor.php +++ b/libraries/default/RequestProcessors/BroodRequestProcessor.php @@ -10,7 +10,6 @@ class BroodRequestProcessor extends GenericRequestProcessor protected $description = ''; // overriden when extending protected $registeredActions = [ 'ToolInterconnection', - 'OneWaySynchronization', ]; public function __construct($loadFromAction=false) { @@ -64,45 +63,3 @@ class ToolInterconnectionProcessor extends BroodRequestProcessor implements Gene return parent::discard($id, $requestData); } } - -class OneWaySynchronizationProcessor extends BroodRequestProcessor implements GenericProcessorActionI { - public $action = 'OneWaySynchronization'; - protected $description; - - public function __construct() { - parent::__construct(); - $this->description = __('Handle cerebrate connection request for another cerebrate instance'); - $this->Broods = TableRegistry::getTableLocator()->get('Broods'); - } - - protected function addValidatorRules($validator) - { - return $validator; - } - - public function create($requestData) { - $this->validateRequestData($requestData); - $requestData['title'] = __('Cerebrate instance {0} requested interconnection', 'Insert cerebrate name'); - return parent::create($requestData); - } - - public function process($id, $requestData) - { - $connectionSuccessfull = false; - $interConnectionResult = []; - if ($connectionSuccessfull) { - $this->discard($id, $requestData); - } - return $this->genActionResult( - $interConnectionResult, - $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection with `{0}` created', 'Insert cerebrate name') : __('Could interconnect with `{0}`.', 'Insert cerebrate name'), - [] - ); - } - - public function discard($id, $requestData) - { - return parent::discard($id, $requestData); - } -} \ No newline at end of file From 5d896266a3072e73603acc94346c7c69f961e1d0 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:07:19 +0200 Subject: [PATCH 11/78] chg: [requestProcessor:user] Renamed template to match processor file --- .../templates/User/{registration.php => Registration.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/default/RequestProcessors/templates/User/{registration.php => Registration.php} (100%) diff --git a/libraries/default/RequestProcessors/templates/User/registration.php b/libraries/default/RequestProcessors/templates/User/Registration.php similarity index 100% rename from libraries/default/RequestProcessors/templates/User/registration.php rename to libraries/default/RequestProcessors/templates/User/Registration.php From 274caff4c81e9491b677eee0e34b79c5ff3b2c06 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:08:53 +0200 Subject: [PATCH 12/78] new: [inbox] Created createProcessorInboxEntry endpoint --- src/Controller/InboxController.php | 39 +++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 8997529..642b978 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -32,7 +32,6 @@ class InboxController extends AppController 'contextFilters' => [ 'fields' => [ 'scope', - 'action', ] ], 'contain' => ['Users'] @@ -82,12 +81,16 @@ class InboxController extends AppController $scope = $request->scope; $action = $request->action; $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); - $processor = $this->requestProcessor->getProcessor($request->scope, $request->action); + if ($scope == 'LocalTool') { + $processor = $this->requestProcessor->getLocalToolProcessor($action, $request->data['toolName']); + } else { + $processor = $this->requestProcessor->getProcessor($scope, $action); + } if ($this->request->is('post')) { $processResult = $processor->process($id, $this->request->getData()); return $processor->genHTTPReply($this, $processResult); } else { - $renderedView = $processor->render($request); + $renderedView = $processor->render($request, $this->request); return $this->response->withStringBody($renderedView); } } @@ -114,4 +117,34 @@ class InboxController extends AppController } $this->set('data', $data); } + + public function createProcessorInboxEntry($scope, $action) + { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(__('Only POST method is accepted')); + } + $entryData = [ + 'origin' => $this->request->clientIp(), + 'user_id' => $this->ACL->getUser()['id'], + ]; + $entryData['data'] = $this->request->data ?? []; + // $entryData['data'] = [ + // 'toolName' => 'MISP', + // 'url' => 'http://localhost:8000', + // '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')); + } + $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); + } } From 285061c4c6864fcd3434e492d6febc1ec0e9cd67 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:09:54 +0200 Subject: [PATCH 13/78] chg: [requestProcessor] Improved processor collection for local tools processor --- src/Model/Table/RequestProcessorTable.php | 79 ++++++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/src/Model/Table/RequestProcessorTable.php b/src/Model/Table/RequestProcessorTable.php index e76fdab..d36e922 100644 --- a/src/Model/Table/RequestProcessorTable.php +++ b/src/Model/Table/RequestProcessorTable.php @@ -4,6 +4,13 @@ namespace App\Model\Table; use App\Model\Table\AppTable; use Cake\Filesystem\Folder; +use Cake\Http\Exception\MethodNotAllowedException; +use Cake\Core\Exception\Exception; + +class MissingRequestProcessorException extends Exception +{ + protected $_defaultCode = 404; +} class RequestProcessorTable extends AppTable { @@ -42,7 +49,19 @@ class RequestProcessorTable extends AppTable throw new \Exception(__('Processor {0}.{1} not found', $scope, $action)); } } - throw new \Exception(__('Processor not found'), 1); + throw new MissingRequestProcessorException(__('Processor not found')); + } + + public function getLocalToolProcessor($action, $toolName) + { + $scope = "LocalTool"; + $specificScope = "{$toolName}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) { + $processor = $this->getProcessor($scope, $action); + } + return $processor; } public function listProcessors($scope=null) @@ -53,7 +72,7 @@ class RequestProcessorTable extends AppTable if (isset($this->requestProcessors[$scope])) { return $this->requestProcessors[$scope]; } else { - throw new \Exception(__('Processors for {0} not found', $scope)); + throw new MissingRequestProcessorException(__('Processors for {0} not found', $scope)); } } } @@ -69,7 +88,7 @@ class RequestProcessorTable extends AppTable $processorMainClassName = str_replace('.php', '', $processorFile); $processorMainClassNameShort = str_replace('RequestProcessor.php', '', $processorFile); $processorMainClass = $this->getProcessorClass($processorDir->pwd() . DS . $processorFile, $processorMainClassName); - if ($processorMainClass !== false) { + if (is_object($processorMainClass)) { $this->requestProcessors[$processorMainClassNameShort] = $processorMainClass; foreach ($this->requestProcessors[$processorMainClassNameShort]->getRegisteredActions() as $registeredAction) { $scope = $this->requestProcessors[$processorMainClassNameShort]->getScope(); @@ -79,17 +98,59 @@ class RequestProcessorTable extends AppTable $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; } } + } else { + $this->requestProcessors[$processorMainClassNameShort] = new \stdClass(); + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction} = new \stdClass(); + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->action = "N/A"; + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->error = $processorMainClass; } } } - + + /** + * getProcessorClass + * + * @param string $filePath + * @param string $processorMainClassName + * @return object|string Object loading success, string containing the error if failure + */ private function getProcessorClass($filePath, $processorMainClassName) { - require_once($filePath); - $reflection = new \ReflectionClass($processorMainClassName); - $processorMainClass = $reflection->newInstance(true); - if ($processorMainClass->checkLoading() === 'Assimilation successful!') { - return $processorMainClass; + try { + require_once($filePath); + try { + $reflection = new \ReflectionClass($processorMainClassName); + } catch (\ReflectionException $e) { + return $e->getMessage(); + } + $processorMainClass = $reflection->newInstance(true); + if ($processorMainClass->checkLoading() === 'Assimilation successful!') { + return $processorMainClass; + } + } catch (Exception $e) { + return $e->getMessage(); } } + + /** + * createInboxEntry + * + * @param Object|Array $processor can either be the processor object or an array containing data to fetch it + * @param Array $data + * @return Array + */ + public function createInboxEntry($processor, $data) + { + if (!is_object($processor) && !is_array($processor)) { + throw new MethodNotAllowedException(__("Invalid processor passed")); + } + if (is_array($processor)) { + if (empty($processor['scope']) || empty($processor['action'])) { + throw new MethodNotAllowedException(__("Invalid data passed. Missing either `scope` or `action`")); + } + $processor = $this->getProcessor('User', 'Registration'); + } + return $processor->create($data); + } } From 14607962f868e451c644006521fe20b19afa0ac0 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:11:23 +0200 Subject: [PATCH 14/78] chg: [requestProcessors:genericProcessor] Use scope and action to get template path --- .../GenericRequestProcessor.php | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/RequestProcessors/GenericRequestProcessor.php index b5d2300..3942bd4 100644 --- a/libraries/default/RequestProcessors/GenericRequestProcessor.php +++ b/libraries/default/RequestProcessors/GenericRequestProcessor.php @@ -17,14 +17,19 @@ class GenericRequestProcessor protected $Inbox; protected $registeredActions = []; protected $validator; - private $processingTemplate = '/genericTemplates/confirm'; - private $processingTemplatesDirectory = ROOT . '/libraries/default/RequestProcessors/templates'; + protected $processingTemplate = '/genericTemplates/confirm'; + protected $processingTemplatesDirectory = ROOT . '/libraries/default/RequestProcessors/templates'; public function __construct($registerActions=false) { $this->Inbox = TableRegistry::getTableLocator()->get('Inbox'); if ($registerActions) { $this->registerActionInProcessor(); } + $this->assignProcessingTemplate(); + } + + private function assignProcessingTemplate() + { $processingTemplatePath = $this->getProcessingTemplatePath(); $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); if ($file->exists()) { @@ -41,14 +46,16 @@ class GenericRequestProcessor { return $this->scope; } - - private function getProcessingTemplatePath() + public function getDescription() + { + return $this->description ?? ''; + } + + protected function getProcessingTemplatePath() { - $class = str_replace('RequestProcessor', '', get_parent_class($this)); - $action = strtolower(str_replace('Processor', '', get_class($this))); return sprintf('%s/%s.php', - $class, - $action + $this->scope, + $this->action ); } From a94a2ec672d9dc987b471cbbedb1b6b3c4ebeaf6 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:11:57 +0200 Subject: [PATCH 15/78] fix: [requestProcessor:genericProcessor] Make sure view builder knows about all request variables --- .../default/RequestProcessors/GenericRequestProcessor.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/RequestProcessors/GenericRequestProcessor.php index 3942bd4..5176b24 100644 --- a/libraries/default/RequestProcessors/GenericRequestProcessor.php +++ b/libraries/default/RequestProcessors/GenericRequestProcessor.php @@ -64,7 +64,7 @@ class GenericRequestProcessor return $this->processingTemplate; } - public function render($request=[]) + public function render($request=[], Cake\Http\ServerRequest $serverRequest) { $processingTemplate = $this->getProcessingTemplate(); $viewVariables = $this->getViewVariables($request); @@ -73,6 +73,7 @@ class GenericRequestProcessor ->setClassName('Monad') ->setTemplate($processingTemplate); $view = $builder->build($viewVariables); + $view->setRequest($serverRequest); return $view->render(); } From 4966e6252ec88b70362da01cd8b6875bfc216a83 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 12:13:15 +0200 Subject: [PATCH 16/78] new: [requestProcessor:localTool] Added generic localTool processor files --- .../LocalToolRequestProcessor.php | 227 ++++++++++++++++++ .../templates/LocalTool/GenericRequest.php | 80 ++++++ 2 files changed, 307 insertions(+) create mode 100644 libraries/default/RequestProcessors/LocalToolRequestProcessor.php create mode 100644 libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php new file mode 100644 index 0000000..ce17083 --- /dev/null +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -0,0 +1,227 @@ +Broods = TableRegistry::getTableLocator()->get('Broods'); + } + + public function create($requestData) + { + return parent::create($requestData); + } + + protected function assignProcessingTemplate($toolName) + { + $processingTemplatePath = sprintf('%s/%s/%s.php', $this->scope, $toolName, $this->action); + $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); + if ($file->exists()) { + $this->processingTemplate = str_replace('.php', '', $processingTemplatePath); + } + $file->close(); + } + + protected function validateToolName($requestData) + { + if (empty($requestData['data']['toolName'])) { + throw new Exception('Error while validating request data. Tool name is missing.'); + } + } + + protected function getIssuerBrood($request) + { + $brood = $this->Broods->find() + ->where(['url' => $request['origin']]) + ->first(); + return $brood; + } + + protected function addBaseValidatorRules($validator) + { + return $validator + ->requirePresence('toolName') + ->notEmpty('toolName', 'A url must be provided') + ->requirePresence('url') + ->notEmpty('url', 'A url must be provided'); + // ->add('url', 'validFormat', [ + // 'rule' => 'url', + // 'message' => 'URL must be valid' + // ]); + } +} + +class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { + public $action = 'IncomingConnectionRequest'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle Phase I of inter-connection when another cerebrate instance performs the request.'); + } + + protected function addValidatorRules($validator) + { + return $this->addBaseValidatorRules($validator); + } + + public function create($requestData) { + $this->validateToolName($requestData); + $this->validateRequestData($requestData); + $requestData['title'] = __('Request for {0} Inter-connection', $requestData['data']['toolName']); + return parent::create($requestData); + } + + public function getViewVariables($request) + { + $request->brood = $this->getIssuerBrood($request); + return [ + 'request' => $request, + 'progressStep' => 0, + ]; + } + + public function process($id, $requestData) + { + $connectionSuccessfull = false; + $interConnectionResult = []; + if ($connectionSuccessfull) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $interConnectionResult, + $connectionSuccessfull, + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} created',$requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + [] + ); + } + + public function discard($id, $requestData) + { + // /!\ TODO: send decline message to remote cerebrate + return parent::discard($id, $requestData); + } +} + +class AcceptedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { + public $action = 'AcceptedRequest'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle Phase II of inter-connection when initial request has been accepted by the remote cerebrate.'); + // $this->Broods = TableRegistry::getTableLocator()->get('Broods'); + } + + protected function addValidatorRules($validator) + { + return $this->addBaseValidatorRules($validator); + } + + public function create($requestData) { + $this->validateToolName($requestData); + $this->validateRequestData($requestData); + $requestData['title'] = __('Inter-connection for {0} has been accepted', $requestData['data']['toolName']); + return parent::create($requestData); + } + + public function getViewVariables($request) + { + $request->brood = $this->getIssuerBrood($request); + return [ + 'request' => $request, + 'progressStep' => 1, + ]; + } + + public function process($id, $requestData) + { + $connectionSuccessfull = false; + $interConnectionResult = []; + if ($connectionSuccessfull) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $interConnectionResult, + $connectionSuccessfull, + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} + +class DeclinedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { + public $action = 'DeclinedRequest'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle Phase II of MISP inter-connection when initial request has been declined by the remote cerebrate.'); + } + + protected function addValidatorRules($validator) + { + return $this->addBaseValidatorRules($validator); + } + + public function create($requestData) { + $this->validateToolName($requestData); + $this->validateRequestData($requestData); + $requestData['title'] = __('Declined inter-connection for {0}', $requestData['data']['toolName']); + return parent::create($requestData); + } + + public function getViewVariables($request) + { + $request->brood = $this->getIssuerBrood($request); + return [ + 'request' => $request, + 'progressStep' => 1, + 'progressVariant' => 'danger', + 'steps' => [ + 1 => ['icon' => 'times', 'text' => __('Request Declined'), 'confirmButton' => __('Clean-up')], + 2 => ['icon' => 'trash', 'text' => __('Clean-up')], + ] + ]; + } + + public function process($id, $requestData) + { + $connectionSuccessfull = false; + $interConnectionResult = []; + if ($connectionSuccessfull) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $interConnectionResult, + $connectionSuccessfull, + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + [] + ); + } + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} diff --git a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php new file mode 100644 index 0000000..2c60181 --- /dev/null +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -0,0 +1,80 @@ + __('Request Sent'), + 'icon' => 'paper-plane', + 'title' => __(''), + 'confirmButton' => __('Accept Request') + ], + [ + 'text' => __('Request Accepted'), + 'icon' => 'check-square', + 'title' => __(''), + 'confirmButton' => __('Finalize Connection') + ], + [ + 'text' => __('Connection done'), + 'icon' => 'exchange-alt', + 'title' => __(''), + ] +]; + +$progressVariant = !empty($progressVariant) ? $progressVariant : 'info'; +$finalSteps = array_replace($defaultSteps, $steps ?? []); +$currentStep = $finalSteps[$progressStep]; +$progress = $this->Bootstrap->progressTimeline([ + 'variant' => $progressVariant, + 'selected' => !empty($progressStep) ? $progressStep : 0, + 'steps' => $finalSteps, +]); + +$table = $this->Bootstrap->table(['small' => true, 'bordered' => false, 'striped' => false, 'hover' => false], [ + 'fields' => [ + ['key' => 'data.toolName', 'label' => __('Tool Name')], + ['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'])); + }] + ], + 'items' => [$request->toArray()], +]); +$form = $this->element('genericElements/Form/genericForm', [ + 'entity' => null, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'model' => 'Inbox', + 'fields' => [], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] +]); + +$requestData = $this->Bootstrap->collapse( + [ + 'title' => __('Inter-connection data'), + 'open' => true, + ], + sprintf('
%s
', json_encode($request['data'], JSON_PRETTY_PRINT)) +); + +$bodyHtml = sprintf('
%s
%s
%s', + $table, + $requestData, + $form +); + +echo $this->Bootstrap->modal([ + 'title' => __('Interconnection Request for {0}', h($request->data['toolName'])), + 'size' => 'lg', + 'type' => 'confirm', + 'bodyHtml' => sprintf('
%s
%s
', + $progress, + $bodyHtml + ), + 'confirmText' => $currentStep['confirmButton'] ?? __('Submit'), +]); From 21bc99e50400a1c4891b87805a618c05e856883a Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 13:32:25 +0200 Subject: [PATCH 17/78] chg: [inbox] Usage of virtual field `local_tool_name` --- .../RequestProcessors/LocalToolRequestProcessor.php | 12 ++++++------ src/Controller/InboxController.php | 2 +- src/Model/Entity/Inbox.php | 9 +++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index ce17083..55b7b3b 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -84,7 +84,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple public function create($requestData) { $this->validateToolName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Request for {0} Inter-connection', $requestData['data']['toolName']); + $requestData['title'] = __('Request for {0} Inter-connection', $requestData['local_tool_name']); return parent::create($requestData); } @@ -107,7 +107,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple return $this->genActionResult( $interConnectionResult, $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} created',$requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} created',$requestData['origin'], $requestData['local_tool_name']) : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']), [] ); } @@ -137,7 +137,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function create($requestData) { $this->validateToolName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Inter-connection for {0} has been accepted', $requestData['data']['toolName']); + $requestData['title'] = __('Inter-connection for {0} has been accepted', $requestData['local_tool_name']); return parent::create($requestData); } @@ -160,7 +160,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene return $this->genActionResult( $interConnectionResult, $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], $requestData['local_tool_name']) : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']), [] ); } @@ -188,7 +188,7 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene public function create($requestData) { $this->validateToolName($requestData); $this->validateRequestData($requestData); - $requestData['title'] = __('Declined inter-connection for {0}', $requestData['data']['toolName']); + $requestData['title'] = __('Declined inter-connection for {0}', $requestData['local_tool_name']); return parent::create($requestData); } @@ -216,7 +216,7 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene return $this->genActionResult( $interConnectionResult, $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], '[toolname]') : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], '[toolname]'), + $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], $requestData['local_tool_name']) : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']), [] ); } diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 642b978..411b155 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -82,7 +82,7 @@ class InboxController extends AppController $action = $request->action; $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); if ($scope == 'LocalTool') { - $processor = $this->requestProcessor->getLocalToolProcessor($action, $request->data['toolName']); + $processor = $this->requestProcessor->getLocalToolProcessor($action, $request->local_tool_name); } else { $processor = $this->requestProcessor->getProcessor($scope, $action); } diff --git a/src/Model/Entity/Inbox.php b/src/Model/Entity/Inbox.php index a8f546e..c382398 100644 --- a/src/Model/Entity/Inbox.php +++ b/src/Model/Entity/Inbox.php @@ -7,5 +7,14 @@ use Cake\ORM\Entity; class Inbox extends AppModel { + protected $_virtual = ['local_tool_name']; + protected function _getLocalToolName() + { + $localToolName = null; + if (!empty($this->data) && !empty($this->data['toolName'])) { + $localToolName = $this->data['toolName']; + } + return $localToolName; + } } From a03b433f2b9c6e64fe7ec7541e28aaf3edbe2c79 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 14:04:17 +0200 Subject: [PATCH 18/78] chg: [requestProcessor] Recover local tool from request --- .../LocalToolRequestProcessor.php | 18 ++++++++++++++++++ .../templates/LocalTool/GenericRequest.php | 12 ++++++++++-- src/Model/Table/LocalToolsTable.php | 6 ++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index 55b7b3b..d672eec 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -16,11 +16,13 @@ class LocalToolRequestProcessor extends GenericRequestProcessor ]; protected $processingTemplate = 'LocalTool/GenericRequest'; protected $Broods; + protected $LocalTools; public function __construct($loadFromAction=false) { parent::__construct($loadFromAction); $this->Broods = TableRegistry::getTableLocator()->get('Broods'); + $this->LocalTools = TableRegistry::getTableLocator()->get('LocalTools'); } public function create($requestData) @@ -53,6 +55,19 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return $brood; } + protected function getConnector($request) + { + try { + $connectorClasses = $this->LocalTools->getConnectorByToolName($request->local_tool_name); + if (!empty($connectorClasses)) { + $connector = $this->LocalTools->extractMeta($connectorClasses)[0]; + } + } catch (Cake\Http\Exception\NotFoundException $e) { + $connector = null; + } + return $connector; + } + protected function addBaseValidatorRules($validator) { return $validator @@ -91,6 +106,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); + $request->connector = $this->getConnector($request); return [ 'request' => $request, 'progressStep' => 0, @@ -144,6 +160,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); + $request->connector = $this->getConnector($request); return [ 'request' => $request, 'progressStep' => 1, @@ -195,6 +212,7 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); + $request->connector = $this->getConnector($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 2c60181..42d0d24 100644 --- a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -30,13 +30,21 @@ $progress = $this->Bootstrap->progressTimeline([ $table = $this->Bootstrap->table(['small' => true, 'bordered' => false, 'striped' => false, 'hover' => false], [ 'fields' => [ - ['key' => 'data.toolName', 'label' => __('Tool Name')], + ['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'])); + return sprintf('%s', + $this->Url->build(['controller' => 'broods', 'action' => 'view', $brood['id']]), + h($brood['name']) + ); }] ], 'items' => [$request->toArray()], diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 9cfc48f..25d124e 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -73,6 +73,12 @@ class LocalToolsTable extends AppTable throw new NotFoundException(__('Invalid connector module action requested.')); } + public function getConnectorByToolName($toolName): array + { + $toolName = sprintf('%sConnector', ucfirst(strtolower($toolName))); + return $this->getConnectors($toolName); + } + public function getConnectors(string $name = null): array { $connectors = []; From 712dd508e18250866f97dd428dab30a8839b6723 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 14:26:07 +0200 Subject: [PATCH 19/78] chg: [migration:inbox] Removed commented lines --- config/Migrations/20210311110021_InboxSystem.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/config/Migrations/20210311110021_InboxSystem.php b/config/Migrations/20210311110021_InboxSystem.php index 65f143a..8357ff7 100644 --- a/config/Migrations/20210311110021_InboxSystem.php +++ b/config/Migrations/20210311110021_InboxSystem.php @@ -56,11 +56,6 @@ class InboxSystem extends AbstractMigration 'null' => false, 'limit' => 191, ]) - // ->addColumn('ip', 'string', [ - // 'limit' => 191, - // 'default' => null, - // 'null' => true, - // ]) ->addColumn('user_id', 'integer', [ 'default' => null, 'null' => true, @@ -92,7 +87,6 @@ class InboxSystem extends AbstractMigration ->addIndex('action') ->addIndex('title') ->addIndex('origin') - // ->addIndex('ip') ->addIndex('created') ->addIndex('user_id'); From af69834727f5a29d9fec6927d1b980b740308332 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 14:26:31 +0200 Subject: [PATCH 20/78] new: [migration:remoteToolConnections] Added migration file --- .../20210612140828_RemoteToolConnections.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 config/Migrations/20210612140828_RemoteToolConnections.php diff --git a/config/Migrations/20210612140828_RemoteToolConnections.php b/config/Migrations/20210612140828_RemoteToolConnections.php new file mode 100644 index 0000000..b75e112 --- /dev/null +++ b/config/Migrations/20210612140828_RemoteToolConnections.php @@ -0,0 +1,84 @@ +table('remote_tool_connections', [ + 'signed' => false, + 'collation' => 'utf8mb4_unicode_ci', + ]); + $table + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'limit' => 10, + 'signed' => false, + ]) + ->addPrimaryKey('id') + ->addColumn('local_tool_id', 'integer', [ + 'null' => false, + 'signed' => false, + 'length' => 10, + ]) + ->addColumn('remote_tool_id', 'integer', [ + 'null' => false, + 'signed' => false, + 'length' => 10, + ]) + ->addColumn('remote_tool_name', 'string', [ + 'null' => false, + 'limit' => 191, + ]) + ->addColumn('brood_id', 'integer', [ + 'null' => false, + 'signed' => false, + 'length' => 10, + ]) + ->addColumn('name', 'string', [ + 'null' => true, + 'limit' => 191, + ]) + ->addColumn('settings', 'text', [ + 'null' => true, + 'limit' => MysqlAdapter::TEXT_LONG + ]) + ->addColumn('status', 'string', [ + 'null' => true, + 'limit' => 32, + 'encoding' => 'ascii', + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + + $table->addForeignKey('local_tool_id', 'local_tools', 'id', ['delete'=> 'CASCADE', 'update'=> 'CASCADE']); + + $table->addIndex('remote_tool_id') + ->addIndex('remote_tool_name') + ->addIndex('status') + ->addIndex('name'); + + $table->create(); + } +} + From 034ff034df5433d3c5a384b91991f40bea3781a7 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 12 Jun 2021 15:29:16 +0200 Subject: [PATCH 21/78] chg: [inbox] Started integrating local tools within the inbox - WiP --- .../LocalToolRequestProcessor.php | 8 +++++++ src/Controller/InboxController.php | 2 +- src/Controller/LocalToolsController.php | 24 ++++++++++--------- src/Model/Table/InboxTable.php | 23 ++++++++++++++++++ src/Model/Table/LocalToolsTable.php | 2 +- templates/LocalTools/connection_request.php | 7 +++++- 6 files changed, 52 insertions(+), 14 deletions(-) diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index d672eec..e73c60c 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -117,6 +117,14 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple { $connectionSuccessfull = false; $interConnectionResult = []; + + $remoteCerebrate = $this->getIssuerBrood($request); + $connector = $this->getConnector($request); + $connectorResult = $connector->acceptConnection($requestData['data']); + $connectorResult['toolName'] = $requestData->local_tool_name; + $urlPath = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; + $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); + if ($connectionSuccessfull) { $this->discard($id, $requestData); } diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 411b155..66e6136 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -118,7 +118,7 @@ class InboxController extends AppController $this->set('data', $data); } - public function createProcessorInboxEntry($scope, $action) + public function createInboxEntry($scope, $action) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(__('Only POST method is accepted')); diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 61f6e92..195c689 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -186,11 +186,8 @@ class LocalToolsController extends AppController }, 'afterFind' => function($data) { foreach ($data as $connector) { - $connector = [ - 'id' => $connector['id'], - 'name' => $connector['name'], - 'connector' => $connector['connector'] - ]; + $connectorClass = array_values($this->LocalTools->getConnectorByConnectionId($connector['id']))[0]; + $connector['toolName'] = $connectorClass->name; } return $data; } @@ -222,18 +219,23 @@ class LocalToolsController extends AppController 'cerebrate_id' => $cerebrate_id, 'remote_tool_id' => $remote_tool_id ]; + $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']); + $postParams = $this->ParamHandler->harvestParams(['local_tool_id', 'tool_name']); 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']; - $result = $this->LocalTools->encodeConnection($params); - // Send message to remote inbox - debug($result); + $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 { - $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)) { diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php index 93a7f22..ca4664f 100644 --- a/src/Model/Table/InboxTable.php +++ b/src/Model/Table/InboxTable.php @@ -62,4 +62,27 @@ class InboxTable extends AppTable return $rules; } + + public function sendRequest($brood, $urlPath, $methodPost = true, $data = []): boolean + { + $http = new Client(); + $config = [ + 'headers' => [ + 'AUTHORIZATION' => $brood->authkey, + 'Accept' => 'application/json' + ], + 'type' => 'json' + ]; + $url = $brood->url . $urlPath; + if ($methodPost) { + $response = $http->post($url, json_encode(data), $config); + } else { + $response = $http->get($brood->url, json_encode(data), $config); + } + if ($response->isOk()) { + return $response; + } else { + throw new NotFoundException(__('Could not post to the requested resource.')); + } + } } diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 25d124e..22dadfb 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -112,7 +112,7 @@ class LocalToolsTable extends AppTable 'connector_description' => $connector_class->description ]; if ($includeConnections) { - $connector['connections'] = $this->healthCheck($connector_type, $connector_class); + // $connector['connections'] = $this->healthCheck($connector_type, $connector_class); } $connectors[] = $connector; } diff --git a/templates/LocalTools/connection_request.php b/templates/LocalTools/connection_request.php index 8f672e7..ddde00d 100644 --- a/templates/LocalTools/connection_request.php +++ b/templates/LocalTools/connection_request.php @@ -16,7 +16,12 @@ 'field' => 'local_tool_id', 'options' => $dropdown, 'type' => 'dropdown' - ] + ], + [ + 'field' => 'tool_name', + 'default' => $data['remoteTool']['toolName'], + 'type' => 'hidden' + ], ], 'submit' => [ 'action' => $this->request->getParam('action') From e1ac7fe2e62f9ed4201b1afd1cc4431af8f64db0 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 14 Jun 2021 08:37:00 +0200 Subject: [PATCH 22/78] chg: [wip] localtools --- .../local_tool_connectors/MispConnector.php | 131 ++++++++++++++++-- .../IndexTable/Fields/function.php | 3 + .../IndexTable/Fields/generic_field.php | 6 + .../ListTopBar/group_search.php | 3 + 4 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 templates/element/genericElements/IndexTable/Fields/function.php diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index d86afad..7a2b0bf 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -71,6 +71,17 @@ class MispConnector extends CommonConnectorTools 'value' ], 'redirect' => 'serverSettingsAction' + ], + 'serversAction' => [ + 'type' => 'index', + 'scope' => 'child', + 'params' => [ + 'quickFilter', + 'limit', + 'page', + 'sort', + 'direction' + ] ] ]; public $version = '0.1'; @@ -248,6 +259,7 @@ class MispConnector extends CommonConnectorTools 'name' => __('Value'), 'sort' => 'value', 'data_path' => 'value', + 'options' => 'options' ], [ 'name' => __('Type'), @@ -288,6 +300,96 @@ class MispConnector extends CommonConnectorTools } } + public function serversAction(array $params): array + { + $params['validParams'] = [ + 'limit' => 'limit', + 'page' => 'page', + 'quickFilter' => 'searchall' + ]; + $urlParams = h($params['connection']['id']) . '/serversAction'; + $response = $this->getData('/servers/index', $params); + $data = $response->getJson(); + if (!empty($data)) { + return [ + 'type' => 'index', + 'data' => [ + 'data' => $data, + 'skip_pagination' => 1, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'additionalUrlParams' => $urlParams, + 'quickFilter' => 'value' + ] + ] + ], + 'fields' => [ + [ + 'name' => 'Id', + 'sort' => 'Server.id', + 'data_path' => 'Server.id', + ], + [ + 'name' => 'Name', + 'sort' => 'Server.name', + 'data_path' => 'Server.name', + ], + [ + 'name' => 'Url', + 'sort' => 'Server.url', + 'data_path' => 'Server.url' + ], + [ + 'name' => 'Pull', + 'sort' => 'Server.pull', + 'element' => 'function', + 'function' => function($row, $context) { + $pull = $context->Hash->extract($row, 'Server.pull')[0]; + $pull_rules = $context->Hash->extract($row, 'Server.pull_rules')[0]; + $pull_rules = json_encode(json_decode($pull_rules, true), JSON_PRETTY_PRINT); + echo sprintf( + '', + h($pull_rules), + $pull ? 'check' : 'times' + ); + } + ], + [ + 'name' => 'Push', + 'element' => 'function', + 'function' => function($row, $context) { + $push = $context->Hash->extract($row, 'Server.push')[0]; + $push_rules = $context->Hash->extract($row, 'Server.push_rules')[0]; + $push_rules = json_encode(json_decode($push_rules, true), JSON_PRETTY_PRINT); + echo sprintf( + '', + h($push_rules), + $push ? 'check' : 'times' + ); + } + ], + [ + 'name' => 'Caching', + 'element' => 'boolean', + 'data_path' => 'Server.caching_enabled' + ] + ], + 'title' => false, + 'description' => false, + 'pull' => 'right' + ] + ]; + } else { + return []; + } + } + public function organisationsAction(array $params): array { $params['validParams'] = [ @@ -515,14 +617,27 @@ class MispConnector extends CommonConnectorTools 'boolean' => 'checkbox', 'numeric' => 'number' ]; - $fields = [ - [ - 'field' => 'value', - 'label' => __('Value'), - 'default' => h($response['value']), - 'type' => $types[$response['type']] - ], - ]; + if (!empty($response['options'])) { + $fields = [ + [ + 'field' => 'value', + 'label' => __('Value'), + 'default' => h($response['value']), + 'type' => 'dropdown', + 'options' => $response['options'], + + ] + ]; + } else { + $fields = [ + [ + 'field' => 'value', + 'label' => __('Value'), + 'default' => h($response['value']), + 'type' => $types[$response['type']] + ] + ]; + } return [ 'data' => [ 'title' => __('Modify server setting'), diff --git a/templates/element/genericElements/IndexTable/Fields/function.php b/templates/element/genericElements/IndexTable/Fields/function.php new file mode 100644 index 0000000..db22a56 --- /dev/null +++ b/templates/element/genericElements/IndexTable/Fields/function.php @@ -0,0 +1,3 @@ + diff --git a/templates/element/genericElements/IndexTable/Fields/generic_field.php b/templates/element/genericElements/IndexTable/Fields/generic_field.php index 2ef0980..abc8eca 100644 --- a/templates/element/genericElements/IndexTable/Fields/generic_field.php +++ b/templates/element/genericElements/IndexTable/Fields/generic_field.php @@ -18,6 +18,12 @@ ); } else { $data = h($data); + if (!empty($field['options'])) { + $options = $this->Hash->extract($row, $field['options']); + if (!empty($options)) { + $data = h($options[$data]); + } + } if (!empty($field['privacy'])) { $data = sprintf( '**************************************** ', diff --git a/templates/element/genericElements/ListTopBar/group_search.php b/templates/element/genericElements/ListTopBar/group_search.php index 7822e07..814ffcc 100644 --- a/templates/element/genericElements/ListTopBar/group_search.php +++ b/templates/element/genericElements/ListTopBar/group_search.php @@ -12,6 +12,9 @@ * - id: element ID for the input field - defaults to quickFilterField */ if (!isset($data['requirement']) || $data['requirement']) { + if (!empty($data['quickFilter'])) { + $quickFilter = $data['quickFilter']; + } $filterEffective = !empty($quickFilter); // No filters will be picked up, thus rendering the filtering useless $filteringButton = ''; if (!empty($data['allowFilering'])) { From 20027f4d697b2ae17f294b3080fe2eb4572c8645 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 14 Jun 2021 10:02:16 +0200 Subject: [PATCH 23/78] fix: [https] errors caught by the health element --- .../local_tool_connectors/MispConnector.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 7a2b0bf..efd480e 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -104,13 +104,20 @@ class MispConnector extends CommonConnectorTools { $settings = json_decode($connection->settings, true); $http = new Client(); - $response = $http->post($settings['url'] . '/users/view/me.json', '{}', [ - 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + try { + $response = $http->post($settings['url'] . '/users/view/me.json', '{}', [ + 'headers' => [ + 'AUTHORIZATION' => $settings['authkey'], + 'Accept' => 'Application/json', + 'Content-type' => 'Application/json' + ] + ]); + } catch (\Exception $e) { + return [ + 'status' => 0, + 'message' => __('Connection issue.') + ]; + } $responseCode = $response->getStatusCode(); if ($response->isOk()) { $status = 1; From 47384925c62302053dbbfaa4637ce0ddd03ca7ac Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 14 Jun 2021 13:03:58 +0200 Subject: [PATCH 24/78] chg: [helpers:bootstrap] Allows passing modal footer buttons --- src/View/Helper/BootstrapHelper.php | 26 ++++++- webroot/js/bootstrap-helper.js | 117 ++++++++++++++++++---------- 2 files changed, 100 insertions(+), 43 deletions(-) diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index 0a93bc0..d0562f1 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -768,7 +768,7 @@ class BoostrapModal extends BootstrapGeneric { function __construct($options) { $this->allowedOptionValues = [ 'size' => ['sm', 'lg', 'xl', ''], - 'type' => ['ok-only','confirm','confirm-success','confirm-warning','confirm-danger'], + 'type' => ['ok-only','confirm','confirm-success','confirm-warning','confirm-danger', 'custom'], 'variant' => array_merge(BootstrapGeneric::$variants, ['']), ]; $this->processOptions($options); @@ -834,7 +834,10 @@ class BoostrapModal extends BootstrapGeneric { private function genFooter() { - $footer = $this->openNode('div', ['class' => array_merge(['modal-footer'], $this->options['footerClass'])]); + $footer = $this->openNode('div', [ + 'class' => array_merge(['modal-footer'], $this->options['footerClass']), + 'data-custom-footer' => $this->options['type'] == 'custom' + ]); if (!empty($this->options['footerHtml'])) { $footer .= $this->options['footerHtml']; } else { @@ -849,6 +852,8 @@ class BoostrapModal extends BootstrapGeneric { return $this->getFooterOkOnly(); } else if (str_contains($this->options['type'], 'confirm')) { return $this->getFooterConfirm(); + } else if ($this->options['type'] == 'custom') { + return $this->getFooterCustom(); } else { return $this->getFooterOkOnly(); } @@ -893,6 +898,23 @@ class BoostrapModal extends BootstrapGeneric { ]))->button(); return $buttonCancel . $buttonConfirm; } + + private function getFooterCustom() + { + $buttons = []; + foreach ($this->options['footerButtons'] as $buttonConfig) { + $buttons[] = (new BoostrapButton([ + 'variant' => h($buttonConfig['variant'] ?? 'primary'), + 'text' => h($buttonConfig['text']), + 'class' => 'modal-confirm-button', + 'params' => [ + 'data-dismiss' => !empty($buttonConfig['clickFunction']) ? '' : 'modal', + 'data-clickFunction' => sprintf('%s', $buttonConfig['clickFunction']) + ] + ]))->button(); + } + return implode('', $buttons); + } } class BoostrapProgress extends BootstrapGeneric { diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index d345beb..8f6108c 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -668,13 +668,17 @@ class ModalFactory { } /** Generate the function that will be called when the user confirm the modal */ - getConfirmationHandlerFunction() { + getConfirmationHandlerFunction(i) { return (evt) => { let confirmFunction = this.options.confirm - if (this.options.APIConfirm) { - const tmpApi = new AJAXApi({ - statusNode: evt.target - }) + const tmpApi = new AJAXApi({ + statusNode: evt.target + }) + if (this.options.APIConfirms) { + if (i !== undefined && this.options.APIConfirms[i] !== undefined) { + confirmFunction = () => { return this.options.APIConfirms[i](tmpApi) } + } + } else if (this.options.APIConfirm) { confirmFunction = () => { return this.options.APIConfirm(tmpApi) } } let confirmResult = confirmFunction(() => { this.hide() }, this, evt) @@ -695,24 +699,73 @@ class ModalFactory { /** Attach the submission click listener for modals that have been generated by raw HTML */ findSubmitButtonAndAddListener() { - let $submitButton = this.$modal.find('.modal-footer #submitButton') - if (!$submitButton[0]) { - $submitButton = this.$modal.find('.modal-footer .modal-confirm-button') - } - if ($submitButton[0]) { - const formID = $submitButton.data('form-id') - let $form - if (formID) { - $form = $(formID) - } else { - $form = this.$modal.find('form') + let $modalFooter = this.$modal.find('.modal-footer') + if ($modalFooter.data('custom-footer')) { // setup basic listener as callback are defined in the template + let $submitButtons = this.$modal.find('.modal-footer .modal-confirm-button') + var selfModal = this; + selfModal.options.APIConfirms = []; + $submitButtons.each(function(i) { + const $submitButton = $(this) + if ($submitButton.data('clickfunction') !== undefined && $submitButton.data('clickfunction') !== '') { + const clickHandler = window[$submitButton.data('clickfunction')] + selfModal.options.APIConfirms[i] = (tmpApi) => { + let clickResult = clickHandler(selfModal, tmpApi) + if (clickResult !== undefined) { + return clickResult + .then((data) => { + if (data.success) { + selfModal.options.POSTSuccessCallback(data) + } else { // Validation error + selfModal.injectFormValidationFeedback(form, data.errors) + return Promise.reject('Validation error'); + } + }) + .catch((errorMessage) => { + selfModal.options.POSTFailCallback(errorMessage) + return Promise.reject(errorMessage); + }) + } + } + } + $submitButton.click(selfModal.getConfirmationHandlerFunction(i)) + }) + } else { + let $submitButton = this.$modal.find('.modal-footer #submitButton') + if (!$submitButton[0]) { + $submitButton = this.$modal.find('.modal-footer .modal-confirm-button') } - if ($submitButton.data('confirmfunction') !== undefined && $submitButton.data('confirmfunction') !== '') { - const clickHandler = window[$submitButton.data('confirmfunction')] - this.options.APIConfirm = (tmpApi) => { - let clickResult = clickHandler(this, tmpApi) - if (clickResult !== undefined) { - return clickResult + if ($submitButton[0]) { + const formID = $submitButton.data('form-id') + let $form + if (formID) { + $form = $(formID) + } else { + $form = this.$modal.find('form') + } + if ($submitButton.data('confirmfunction') !== undefined && $submitButton.data('confirmfunction') !== '') { + const clickHandler = window[$submitButton.data('confirmfunction')] + this.options.APIConfirm = (tmpApi) => { + let clickResult = clickHandler(this, tmpApi) + if (clickResult !== undefined) { + return clickResult + .then((data) => { + if (data.success) { + this.options.POSTSuccessCallback(data) + } else { // Validation error + this.injectFormValidationFeedback(form, data.errors) + return Promise.reject('Validation error'); + } + }) + .catch((errorMessage) => { + this.options.POSTFailCallback(errorMessage) + return Promise.reject(errorMessage); + }) + } + } + } else { + $submitButton[0].removeAttribute('onclick') + this.options.APIConfirm = (tmpApi) => { + return tmpApi.postForm($form[0]) .then((data) => { if (data.success) { this.options.POSTSuccessCallback(data) @@ -727,26 +780,8 @@ class ModalFactory { }) } } - } else { - $submitButton[0].removeAttribute('onclick') - this.options.APIConfirm = (tmpApi) => { - return tmpApi.postForm($form[0]) - .then((data) => { - if (data.success) { - this.options.POSTSuccessCallback(data) - } else { // Validation error - this.injectFormValidationFeedback(form, data.errors) - return Promise.reject('Validation error'); - } - }) - .catch((errorMessage) => { - this.options.POSTFailCallback(errorMessage) - return Promise.reject(errorMessage); - }) - } + $submitButton.click(this.getConfirmationHandlerFunction()) } - $submitButton.click(this.getConfirmationHandlerFunction()) - } } } From 63e9dd1644fe9ecdc74d76ac91462b2730248346 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 14 Jun 2021 13:25:49 +0200 Subject: [PATCH 25/78] chg: [genericRequestProcessor] Changed signature of `process` function --- .../default/RequestProcessors/BroodRequestProcessor.php | 2 +- .../default/RequestProcessors/GenericRequestProcessor.php | 2 +- .../default/RequestProcessors/LocalToolRequestProcessor.php | 6 +++--- .../default/RequestProcessors/ProposalRequestProcessor.php | 2 +- .../RequestProcessors/SynchronisationRequestProcessor.php | 2 +- .../RequestProcessors/TemplateRequestProcessor.php.template | 2 +- .../default/RequestProcessors/UserRequestProcessor.php | 2 +- src/Controller/InboxController.php | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/default/RequestProcessors/BroodRequestProcessor.php b/libraries/default/RequestProcessors/BroodRequestProcessor.php index b598705..0ea01bb 100644 --- a/libraries/default/RequestProcessors/BroodRequestProcessor.php +++ b/libraries/default/RequestProcessors/BroodRequestProcessor.php @@ -43,7 +43,7 @@ class ToolInterconnectionProcessor extends BroodRequestProcessor implements Gene return parent::create($requestData); } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $connectionSuccessfull = false; $interConnectionResult = []; diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/RequestProcessors/GenericRequestProcessor.php index 5176b24..1bfd974 100644 --- a/libraries/default/RequestProcessors/GenericRequestProcessor.php +++ b/libraries/default/RequestProcessors/GenericRequestProcessor.php @@ -8,7 +8,7 @@ use Cake\View\ViewBuilder; interface GenericProcessorActionI { public function create($requestData); - public function process($requestID, $serverRequest); + public function process($requestID, $serverRequest, $inboxRequest); public function discard($requestID ,$requestData); } diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index e73c60c..0406779 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -113,7 +113,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple ]; } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $connectionSuccessfull = false; $interConnectionResult = []; @@ -175,7 +175,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene ]; } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $connectionSuccessfull = false; $interConnectionResult = []; @@ -232,7 +232,7 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene ]; } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $connectionSuccessfull = false; $interConnectionResult = []; diff --git a/libraries/default/RequestProcessors/ProposalRequestProcessor.php b/libraries/default/RequestProcessors/ProposalRequestProcessor.php index 3421edf..7eab1c8 100644 --- a/libraries/default/RequestProcessors/ProposalRequestProcessor.php +++ b/libraries/default/RequestProcessors/ProposalRequestProcessor.php @@ -43,7 +43,7 @@ class ProposalEditProcessor extends ProposalRequestProcessor implements GenericP return parent::create($requestData); } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $proposalAccepted = false; $saveResult = []; diff --git a/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php b/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php index ae63ad5..b869e09 100644 --- a/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php +++ b/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php @@ -43,7 +43,7 @@ class DataExchangeProcessor extends SynchronisationRequestProcessor implements G return parent::create($requestData); } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $dataExchangeAccepted = false; $saveResult = []; diff --git a/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template b/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template index 6adb4c0..fb76d2c 100644 --- a/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template +++ b/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template @@ -43,7 +43,7 @@ class SCOPE_ACTION_Processor extends ProposalRequestProcessor implements Generic return parent::create($requestData); } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { $proposalAccepted = false; $saveResult = []; diff --git a/libraries/default/RequestProcessors/UserRequestProcessor.php b/libraries/default/RequestProcessors/UserRequestProcessor.php index 3120bde..eee795e 100644 --- a/libraries/default/RequestProcessors/UserRequestProcessor.php +++ b/libraries/default/RequestProcessors/UserRequestProcessor.php @@ -80,7 +80,7 @@ class RegistrationProcessor extends UserRequestProcessor implements GenericProce ]; } - public function process($id, $requestData) + public function process($id, $requestData, $inboxRequest) { if ($requestData['individual_id'] == -1) { $individual = $this->Users->Individuals->newEntity([ diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 66e6136..a4948aa 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -87,7 +87,7 @@ class InboxController extends AppController $processor = $this->requestProcessor->getProcessor($scope, $action); } if ($this->request->is('post')) { - $processResult = $processor->process($id, $this->request->getData()); + $processResult = $processor->process($id, $this->request->getData(), $request); return $processor->genHTTPReply($this, $processResult); } else { $renderedView = $processor->render($request, $this->request); From 1d293c350f6c4ee80129c2bc353c2415ab8c8cd1 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 14 Jun 2021 13:30:26 +0200 Subject: [PATCH 26/78] chg: [requestProcessor] Improved integration with local tool connectors --- .../LocalToolRequestProcessor.php | 117 ++++++++++++++---- .../templates/LocalTool/GenericRequest.php | 53 +++++++- 2 files changed, 141 insertions(+), 29 deletions(-) diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index 0406779..6cd9e93 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -60,7 +60,7 @@ class LocalToolRequestProcessor extends GenericRequestProcessor try { $connectorClasses = $this->LocalTools->getConnectorByToolName($request->local_tool_name); if (!empty($connectorClasses)) { - $connector = $this->LocalTools->extractMeta($connectorClasses)[0]; + $connector = array_values($connectorClasses)[0]; } } catch (Cake\Http\Exception\NotFoundException $e) { $connector = null; @@ -68,12 +68,25 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return $connector; } + 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; + } + return !is_null($connectorMeta) ? $connectorMeta : []; + } + protected function addBaseValidatorRules($validator) { return $validator ->requirePresence('toolName') ->notEmpty('toolName', 'A url must be provided') - ->requirePresence('url') + ->requirePresence('url') // url -> cerebrate_url ->notEmpty('url', 'A url must be provided'); // ->add('url', 'validFormat', [ // 'rule' => 'url', @@ -106,7 +119,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnector($request); + $request->connector = $this->getConnectorMeta($request); return [ 'request' => $request, 'progressStep' => 0, @@ -115,32 +128,76 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple public function process($id, $requestData, $inboxRequest) { - $connectionSuccessfull = false; + /** + * /!\ Should how should sent message be? be fire and forget? Only for delined? + */ $interConnectionResult = []; - - $remoteCerebrate = $this->getIssuerBrood($request); - $connector = $this->getConnector($request); - $connectorResult = $connector->acceptConnection($requestData['data']); - $connectorResult['toolName'] = $requestData->local_tool_name; - $urlPath = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; - $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); - - if ($connectionSuccessfull) { - $this->discard($id, $requestData); + $remoteCerebrate = $this->getIssuerBrood($inboxRequest); + $connector = $this->getConnector($inboxRequest); + if (!empty($requestData['is_discard'])) { // -> declined + $connectorResult = $this->declineConnection($connector, $remoteCerebrate, $inboxRequest['data']); // Fire-and-forget? + $connectionSuccessfull = true; + $resultTitle = __('Could not sent declined message to `{0}`\'s for {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $errors = []; + if ($connectionSuccessfull) { + $resultTitle = __('Declined message successfully sent to `{0}`\'s for {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $this->discard($id, $inboxRequest); + } + } else { + $connectorResult = $this->acceptConnection($connector, $remoteCerebrate, $inboxRequest['data']); + $connectionSuccessfull = false; + $connectionData = []; + $resultTitle = __('Could not inter-connect `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $errors = []; + if ($connectionSuccessfull) { + $resultTitle = __('Interconnection for `{0}`\'s {1} created', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $this->discard($id, $inboxRequest); + } } return $this->genActionResult( - $interConnectionResult, + $connectionData, $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} created',$requestData['origin'], $requestData['local_tool_name']) : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']), - [] + $resultTitle, + $errors ); } public function discard($id, $requestData) { - // /!\ TODO: send decline message to remote cerebrate return parent::discard($id, $requestData); } + + protected function acceptConnection($connector, $remoteCerebrate, $requestData) + { + $connectorResult = $connector->acceptConnection($requestData['data']); + $connectorResult['toolName'] = $requestData->local_tool_name; + $response = $this->sendAcceptedRequestToRemote($remoteCerebrate, $connectorResult); + // change state if sending fails + // add the entry to the outbox if sending fails. + return $response; + } + + protected function declineConnection($connector, $remoteCerebrate, $requestData) + { + $connectorResult = $connector->declineConnection($requestData['data']); + $connectorResult['toolName'] = $requestData->local_tool_name; + $response = $this->sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult); + return $response; + } + + protected function sendAcceptedRequestToRemote($remoteCerebrate, $connectorResult) + { + $urlPath = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; + $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); + return $response; + } + + protected function sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult) + { + $urlPath = '/inbox/createInboxEntry/LocalTool/DeclinedRequest'; + $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); + return $response; + } } class AcceptedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { @@ -168,7 +225,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnector($request); + $request->connector = $this->getConnectorMeta($request); return [ 'request' => $request, 'progressStep' => 1, @@ -177,16 +234,22 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function process($id, $requestData, $inboxRequest) { + $connector = $this->getConnector($request); + $remoteCerebrate = $this->getIssuerBrood($request); + $connectorResult = $this->finalizeConnection($connector, $remoteCerebrate, $requestData['data']); $connectionSuccessfull = false; - $interConnectionResult = []; + $connectionData = []; + $resultTitle = __('Could not finalize inter-connection for `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']); + $errors = []; if ($connectionSuccessfull) { + $resultTitle = __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], $requestData['local_tool_name']); $this->discard($id, $requestData); } return $this->genActionResult( - $interConnectionResult, + $connectionData, $connectionSuccessfull, - $connectionSuccessfull ? __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], $requestData['local_tool_name']) : __('Could not inter-connect `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']), - [] + $resultTitle, + $errors ); } @@ -194,6 +257,12 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene { return parent::discard($id, $requestData); } + + protected function finalizeConnection($connector, $remoteCerebrate, $requestData) + { + $connectorResult = $connector->finaliseConnection($requestData['data']); + return $connectorResult; + } } class DeclinedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { @@ -220,7 +289,7 @@ class DeclinedRequestProcessor extends LocalToolRequestProcessor implements Gene public function getViewVariables($request) { $request->brood = $this->getIssuerBrood($request); - $request->connector = $this->getConnector($request); + $request->connector = $this->getConnectorMeta($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 42d0d24..72b8d94 100644 --- a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -4,7 +4,8 @@ $defaultSteps = [ 'text' => __('Request Sent'), 'icon' => 'paper-plane', 'title' => __(''), - 'confirmButton' => __('Accept Request') + 'confirmButton' => __('Accept Request'), + 'canDiscard' => true, ], [ 'text' => __('Request Accepted'), @@ -19,6 +20,8 @@ $defaultSteps = [ ] ]; +$footerButtons = []; + $progressVariant = !empty($progressVariant) ? $progressVariant : 'info'; $finalSteps = array_replace($defaultSteps, $steps ?? []); $currentStep = $finalSteps[$progressStep]; @@ -28,6 +31,23 @@ $progress = $this->Bootstrap->progressTimeline([ 'steps' => $finalSteps, ]); +$footerButtons[] = [ + 'clickFunction' => 'cancel', + 'variant' => 'secondary', + 'text' => __('Cancel'), +]; +if (!empty($currentStep['canDiscard'])) { + $footerButtons[] = [ + 'clickFunction' => 'discard', + 'variant' => 'danger', + 'text' => __('Decline Request'), + ]; +} +$footerButtons[] = [ + 'clickFunction' => 'accept', + 'text' => $currentStep['confirmButton'] ?? __('Submit'), +]; + $table = $this->Bootstrap->table(['small' => true, 'bordered' => false, 'striped' => false, 'hover' => false], [ 'fields' => [ ['key' => 'connector', 'label' => __('Tool Name'), 'formatter' => function($connector, $row) { @@ -55,7 +75,13 @@ $form = $this->element('genericElements/Form/genericForm', [ 'raw' => true, 'data' => [ 'model' => 'Inbox', - 'fields' => [], + 'fields' => [ + [ + 'field' => 'is_discard', + 'type' => 'checkbox', + 'default' => false + ] + ], 'submit' => [ 'action' => $this->request->getParam('action') ] @@ -70,7 +96,7 @@ $requestData = $this->Bootstrap->collapse( sprintf('
%s
', json_encode($request['data'], JSON_PRETTY_PRINT)) ); -$bodyHtml = sprintf('
%s
%s
%s', +$bodyHtml = sprintf('
%s
%s
%s
', $table, $requestData, $form @@ -79,10 +105,27 @@ $bodyHtml = sprintf('
%s
%s
%s', echo $this->Bootstrap->modal([ 'title' => __('Interconnection Request for {0}', h($request->data['toolName'])), 'size' => 'lg', - 'type' => 'confirm', + 'type' => 'custom', 'bodyHtml' => sprintf('
%s
%s
', $progress, $bodyHtml ), - 'confirmText' => $currentStep['confirmButton'] ?? __('Submit'), + 'footerButtons' => $footerButtons ]); + +?> + + \ No newline at end of file From 048842263a7687b798d9f7bd2ec4ca7d24b78cd3 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 14 Jun 2021 14:13:38 +0200 Subject: [PATCH 27/78] fix: [localTools] Uncommented health-check --- src/Model/Table/LocalToolsTable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 22dadfb..25d124e 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -112,7 +112,7 @@ class LocalToolsTable extends AppTable 'connector_description' => $connector_class->description ]; if ($includeConnections) { - // $connector['connections'] = $this->healthCheck($connector_type, $connector_class); + $connector['connections'] = $this->healthCheck($connector_type, $connector_class); } $connectors[] = $connector; } From 974ed02e9b687523a09b192f0478691a29962a13 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 14 Jun 2021 16:46:53 +0200 Subject: [PATCH 28/78] chg: [requestProcessors] Usage of connector name, connector/user/broods validations & UI improvements --- .../LocalToolRequestProcessor.php | 111 +++++++++++++----- .../templates/LocalTool/GenericRequest.php | 30 +++-- src/Controller/InboxController.php | 28 +++-- src/Controller/LocalToolsController.php | 13 +- src/Model/Entity/Inbox.php | 8 +- src/Model/Table/InboxTable.php | 26 +++- src/Model/Table/LocalToolsTable.php | 10 ++ src/Model/Table/RequestProcessorTable.php | 4 +- 8 files changed, 167 insertions(+), 63 deletions(-) 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) { From f7b3e712eb03507c1a829aa72449a9c2d1c6bac6 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 16 Jun 2021 09:47:26 +0200 Subject: [PATCH 29/78] fix: [localTools] Fixed typos --- src/Controller/LocalToolsController.php | 2 +- src/Model/Entity/Inbox.php | 6 +++--- templates/LocalTools/connection_request.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index b78ae97..a49e59f 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -186,7 +186,7 @@ class LocalToolsController extends AppController }, 'afterFind' => function($data) { foreach ($data as $connector) { - $connectorById = $this->LocalTools->getConnectorByConnectionId($connector['id']) + $connectorById = $this->LocalTools->getConnectorByConnectionId($connector['id']); $className = array_keys($connectorById)[0]; $connector['connectorName'] = $className; } diff --git a/src/Model/Entity/Inbox.php b/src/Model/Entity/Inbox.php index 8192d4e..820e29d 100644 --- a/src/Model/Entity/Inbox.php +++ b/src/Model/Entity/Inbox.php @@ -11,10 +11,10 @@ class Inbox extends AppModel protected function _getLocalToolConnectorName() { - $localToolName = null; + $localConnectorName = null; if (!empty($this->data) && !empty($this->data['connectorName'])) { - $localToolName = $this->data['connectorName']; + $localConnectorName = $this->data['connectorName']; } - return $localToolName; + return $localConnectorName; } } diff --git a/templates/LocalTools/connection_request.php b/templates/LocalTools/connection_request.php index ddde00d..0b8a52e 100644 --- a/templates/LocalTools/connection_request.php +++ b/templates/LocalTools/connection_request.php @@ -19,7 +19,7 @@ ], [ 'field' => 'tool_name', - 'default' => $data['remoteTool']['toolName'], + 'default' => $data['remoteTool']['connectorName'], 'type' => 'hidden' ], ], From 216d04723fd3e55828647a176e189c901ac2158a Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 16 Jun 2021 09:48:07 +0200 Subject: [PATCH 30/78] chg: [localToolConnector] Generic HTTP client and `skip_ssl` feature --- .../local_tool_connectors/MispConnector.php | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index efd480e..35c6ccc 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -100,18 +100,33 @@ class MispConnector extends CommonConnectorTools } } + public function getHTTPClient(Object $connection): Object + { + $settings = json_decode($connection->settings, true); + $options = [ + 'headers' => [ + 'AUTHORIZATION' => $settings['authkey'], + 'Accept' => 'Application/json', + 'Content-type' => 'Application/json' + ], + ]; + if (!empty($settings['skip_ssl'])) { + $options['ssl_verify_peer'] = false; + $options['ssl_verify_host'] = false; + $options['ssl_verify_peer_name'] = false; + $options['ssl_allow_self_signed'] = true; + } + $http = new Client($options); + return $http; + } + public function health(Object $connection): array { $settings = json_decode($connection->settings, true); - $http = new Client(); + $http = $this->getHTTPClient($connection); + try { - $response = $http->post($settings['url'] . '/users/view/me.json', '{}', [ - 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $http->post($settings['url'] . '/users/view/me.json', '{}'); } catch (\Exception $e) { return [ 'status' => 0, @@ -142,7 +157,7 @@ class MispConnector extends CommonConnectorTools throw new NotFoundException(__('No connection object received.')); } $settings = json_decode($params['connection']->settings, true); - $http = new Client(); + $http = $this->getHTTPClient($params['connection']); if (!empty($params['sort'])) { $list = explode('.', $params['sort']); $params['sort'] = end($list); @@ -174,7 +189,7 @@ class MispConnector extends CommonConnectorTools throw new NotFoundException(__('No connection object received.')); } $settings = json_decode($params['connection']->settings, true); - $http = new Client(); + $http = $this->getHTTPClient($params['connection']); $url = $this->urlAppendParams($url, $params); $response = $http->post($settings['url'] . $url, json_encode($params['body']), [ 'headers' => [ From 7757bec29224b446ca88796d58165ec43960078b Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 17 Jun 2021 08:54:09 +0200 Subject: [PATCH 31/78] fix: [API] various fixes --- src/Application.php | 4 +++- src/Controller/AppController.php | 3 +++ src/Controller/Component/CRUDComponent.php | 26 ++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Application.php b/src/Application.php index 8eb1704..a3bb1a6 100644 --- a/src/Application.php +++ b/src/Application.php @@ -27,6 +27,7 @@ use Authentication\AuthenticationService; use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; +use Cake\Http\Middleware\BodyParserMiddleware; use Psr\Http\Message\ServerRequestInterface; /** @@ -87,7 +88,8 @@ class Application extends BaseApplication implements AuthenticationServiceProvid // using it's second constructor argument: // `new RoutingMiddleware($this, '_cake_routes_')` ->add(new RoutingMiddleware($this)) - ->add(new AuthenticationMiddleware($this)); + ->add(new AuthenticationMiddleware($this)) + ->add(new BodyParserMiddleware()); return $middlewareQueue; } diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 70116d9..66bae56 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -91,6 +91,9 @@ class AppController extends Controller $this->loadModel('Users'); $this->Users->checkForNewInstance(); $this->authApiUser(); + if ($this->ParamHandler->isRest()) { + $this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]); + } $this->ACL->setPublicInterfaces(); if (!empty($this->request->getAttribute('identity'))) { $user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [ diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 31de16c..09bbe0e 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -10,6 +10,7 @@ use Cake\View\ViewBuilder; class CRUDComponent extends Component { + protected $components = ['RestResponse']; public function initialize(array $config): void { @@ -58,7 +59,7 @@ class CRUDComponent extends Component $data = $this->Table->{$options['afterFind']}($data); } } - $this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json'); + $this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json'); } else { $this->Controller->loadComponent('Paginator'); $data = $this->Controller->Paginator->paginate($query); @@ -147,9 +148,9 @@ class CRUDComponent extends Component } else if ($this->Controller->ParamHandler->isAjax()) { if (!empty($params['displayOnSuccess'])) { $displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]); - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message, ['displayOnSuccess' => $displayOnSuccess]); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message, ['displayOnSuccess' => $displayOnSuccess]); } else { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message); } } else { $this->Controller->Flash->success($message); @@ -168,8 +169,9 @@ class CRUDComponent extends Component empty($validationMessage) ? '' : PHP_EOL . __('Reason:{0}', $validationMessage) ); if ($this->Controller->ParamHandler->isRest()) { + $this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json'); } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxFailResponse($this->ObjectAlias, 'add', $data, $message, $validationMessage); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'add', $data, $message, $validationMessage); } else { $this->Controller->Flash->error($message); } @@ -246,7 +248,7 @@ class CRUDComponent extends Component if ($this->Controller->ParamHandler->isRest()) { $this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json'); } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'edit', $savedData, $message); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'edit', $savedData, $message); } else { $this->Controller->Flash->success($message); if (empty($params['redirect'])) { @@ -263,7 +265,7 @@ class CRUDComponent extends Component ); if ($this->Controller->ParamHandler->isRest()) { } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxFailResponse($this->ObjectAlias, 'edit', $data, $message, $data->getErrors()); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'edit', $data, $message, $data->getErrors()); } else { $this->Controller->Flash->error($message); } @@ -333,7 +335,7 @@ class CRUDComponent extends Component $data = $params['afterFind']($data); } if ($this->Controller->ParamHandler->isRest()) { - $this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json'); + $this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json'); } $this->Controller->set('entity', $data); } @@ -350,7 +352,7 @@ class CRUDComponent extends Component if ($this->Controller->ParamHandler->isRest()) { $this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json'); } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'delete', $data, $message); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'delete', $data, $message); } else { $this->Controller->Flash->success($message); $this->Controller->redirect($this->Controller->referer()); @@ -600,7 +602,7 @@ class CRUDComponent extends Component if ($this->Controller->ParamHandler->isRest()) { $this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json'); } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'toggle', $savedData, $message); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'toggle', $savedData, $message); } else { $this->Controller->Flash->success($message); if (empty($params['redirect'])) { @@ -618,7 +620,7 @@ class CRUDComponent extends Component ); if ($this->Controller->ParamHandler->isRest()) { } else if ($this->Controller->ParamHandler->isAjax()) { - $this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxFailResponse($this->ObjectAlias, 'toggle', $message, $validationMessage); + $this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($this->ObjectAlias, 'toggle', $message, $validationMessage); } else { $this->Controller->Flash->error($message); if (empty($params['redirect'])) { @@ -644,10 +646,10 @@ class CRUDComponent extends Component if ($this->request->is('post')) { $data[$fieldName] = $data[$fieldName] ? true : false; $this->Table->save($data); - $this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData(['value' => $data[$fieldName]], 'json'); + $this->Controller->restResponsePayload = $this->RestResponse->viewData(['value' => $data[$fieldName]], 'json'); } else { if ($this->Controller->ParamHandler->isRest()) { - $this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData(['value' => $data[$fieldName]], 'json'); + $this->Controller->restResponsePayload = $this->RestResponse->viewData(['value' => $data[$fieldName]], 'json'); } else { $this->Controller->set('fieldName', $fieldName); $this->Controller->set('currentValue', $data[$fieldName]); From 1f8010a6ec278e6020f5479bd5ab526e2cbf2064 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Thu, 17 Jun 2021 14:12:17 +0200 Subject: [PATCH 32/78] chg: [application] Added bodyParserMiddleware to parse JSON bodies --- src/Application.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Application.php b/src/Application.php index 8eb1704..2da0df1 100644 --- a/src/Application.php +++ b/src/Application.php @@ -21,6 +21,7 @@ use Cake\Core\Exception\MissingPluginException; use Cake\Error\Middleware\ErrorHandlerMiddleware; use Cake\Http\BaseApplication; use Cake\Http\MiddlewareQueue; +use Cake\Http\Middleware\BodyParserMiddleware; use Cake\Routing\Middleware\AssetMiddleware; use Cake\Routing\Middleware\RoutingMiddleware; use Authentication\AuthenticationService; @@ -28,7 +29,6 @@ use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; use Psr\Http\Message\ServerRequestInterface; - /** * Application setup class. * @@ -87,7 +87,8 @@ class Application extends BaseApplication implements AuthenticationServiceProvid // using it's second constructor argument: // `new RoutingMiddleware($this, '_cake_routes_')` ->add(new RoutingMiddleware($this)) - ->add(new AuthenticationMiddleware($this)); + ->add(new AuthenticationMiddleware($this)) + ->add(new BodyParserMiddleware()); return $middlewareQueue; } From e1ebbc125a0b963fde7dd7a6db6a27f491eda411 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Thu, 17 Jun 2021 14:13:10 +0200 Subject: [PATCH 33/78] chg: [inbox:localTool] Usage of localTools in the inbox to process connection requests - WiP --- .../LocalToolRequestProcessor.php | 103 +++++++++++++----- src/Controller/AppController.php | 4 + src/Controller/InboxController.php | 9 +- src/Controller/LocalToolsController.php | 22 +++- .../CommonConnectorTools.php | 1 + .../local_tool_connectors/MispConnector.php | 71 +++++++----- src/Model/Table/BroodsTable.php | 56 ++++++++++ src/Model/Table/InboxTable.php | 25 +---- src/Model/Table/LocalToolsTable.php | 38 +++++-- webroot/js/bootstrap-helper.js | 16 ++- 10 files changed, 239 insertions(+), 106 deletions(-) diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index 0320195..e5160bf 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -60,6 +60,13 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return $brood; } + protected function getConnection($requestData) + { + $local_tool_id = $requestData['remote_tool_id']; // local_tool_id is actually the remote_tool_id for the sender + $connection = $this->LocalTools->find()->where(['id' => $local_tool_id])->first(); + return $connection; + } + protected function filterAlignmentsForBrood($individual, $brood) { foreach ($individual->alignments as $i => $alignment) { @@ -128,13 +135,34 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return $request; } + protected function genBroodParam($remoteCerebrate, $connection, $connector, $requestData) + { + $local_tool_id = $requestData['remote_tool_id']; // local_tool_id is actually the remote_tool_id for the sender + $remote_tool_id = $requestData['local_tool_id']; // remote_tool_id is actually the local_tool_id for the sender + $remote_org = $this->Broods->Organisations->find()->where(['id' => $remoteCerebrate->organisation_id])->first(); + return [ + 'remote_tool' => [ + 'id' => $remote_tool_id, + 'connector' => $connector->connectorName, + ], + 'remote_org' => $remote_org, + 'remote_tool_data' => $requestData, + 'remote_cerebrate' => $remoteCerebrate, + 'connection' => $connection, + ]; + } + protected function addBaseValidatorRules($validator) { return $validator ->requirePresence('connectorName') ->notEmpty('connectorName', 'The connector name must be provided') ->requirePresence('cerebrateURL') - ->notEmpty('cerebrateURL', 'A url must be provided'); + ->notEmpty('cerebrateURL', 'A url must be provided') + ->requirePresence('local_tool_id') + ->numeric('local_tool_id', 'A local_tool_id must be provided') + ->requirePresence('remote_tool_id') + ->numeric('remote_tool_id', 'A remote_tool_id must be provided'); // ->add('url', 'validFormat', [ // 'rule' => 'url', // 'message' => 'URL must be valid' @@ -191,18 +219,23 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple $this->discard($id, $inboxRequest); } } else { - $connectorResult = $this->acceptConnection($connector, $remoteCerebrate, $inboxRequest['data']); - $connectionSuccessfull = false; - $connectionData = []; - $resultTitle = __('Could not inter-connect `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $errors = []; + $connectorResult = []; + try { + $connectorResult = $this->acceptConnection($connector, $remoteCerebrate, $inboxRequest['data']); + $connectionSuccessfull = true; + } catch (\Throwable $th) { + $connectionSuccessfull = false; + $errors = $th->getMessage(); + } + $resultTitle = __('Could not inter-connect `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); if ($connectionSuccessfull) { $resultTitle = __('Interconnection for `{0}`\'s {1} created', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $this->discard($id, $inboxRequest); } } return $this->genActionResult( - $connectionData, + $connectorResult, $connectionSuccessfull, $resultTitle, $errors @@ -216,34 +249,36 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple protected function acceptConnection($connector, $remoteCerebrate, $requestData) { - $connectorResult = $connector->acceptConnection($requestData['data']); - $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. + $connection = $this->getConnection($requestData); + $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); + $connectorResult = $connector->acceptConnection($params); + $response = $this->sendAcceptedRequestToRemote($params, $connectorResult); return $response; } protected function declineConnection($connector, $remoteCerebrate, $requestData) { - $connectorResult = $connector->declineConnection($requestData['data']); - $connectorResult['connectorName'] = $requestData->local_tool_name; - $response = $this->sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult); + $connection = $this->getConnection($requestData); + $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); + $connectorResult = $connector->declineConnection($params); + $response = $this->sendDeclinedRequestToRemote($params, $connectorResult); return $response; } - protected function sendAcceptedRequestToRemote($remoteCerebrate, $connectorResult) + protected function sendAcceptedRequestToRemote($params, $connectorResult) { - $urlPath = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; - $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); - return $response; + $response = $this->Broods->sendLocalToolAcceptedRequest($params, $connectorResult); + // change state if sending fails + // add the entry to the outbox if sending fails. + return $response->getJson(); } protected function sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult) { - $urlPath = '/inbox/createInboxEntry/LocalTool/DeclinedRequest'; - $response = $this->Inbox->sendRequest($remoteCerebrate, $urlPath, true, $connectorResult); - return $response; + $response = $this->Broods->sendLocalToolDeclinedRequest($params, $connectorResult); + // change state if sending fails + // add the entry to the outbox if sending fails. + return $response->getJson(); } } @@ -254,7 +289,6 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function __construct() { parent::__construct(); $this->description = __('Handle Phase II of inter-connection when initial request has been accepted by the remote cerebrate.'); - // $this->Broods = TableRegistry::getTableLocator()->get('Broods'); } protected function addValidatorRules($validator) @@ -281,15 +315,22 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene public function process($id, $requestData, $inboxRequest) { - $connector = $this->getConnector($request); - $remoteCerebrate = $this->getIssuerBrood($request); - $connectorResult = $this->finalizeConnection($connector, $remoteCerebrate, $requestData['data']); - $connectionSuccessfull = false; - $connectionData = []; - $resultTitle = __('Could not finalize inter-connection for `{0}`\'s {1}', $requestData['origin'], $requestData['local_tool_name']); + $connector = $this->getConnector($inboxRequest); + $remoteCerebrate = $this->getIssuerBrood($inboxRequest); + $errors = []; + $connectorResult = []; + try { + $connectorResult = $this->finalizeConnection($connector, $remoteCerebrate, $inboxRequest['data']); + $connectionSuccessfull = true; + } catch (\Throwable $th) { + $connectionSuccessfull = false; + $errors = $th->getMessage(); + } + $connectionData = []; + $resultTitle = __('Could not finalize inter-connection for `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); if ($connectionSuccessfull) { - $resultTitle = __('Interconnection for `{0}`\'s {1} finalized', $requestData['origin'], $requestData['local_tool_name']); + $resultTitle = __('Interconnection for `{0}`\'s {1} finalized', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $this->discard($id, $requestData); } return $this->genActionResult( @@ -307,7 +348,9 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene protected function finalizeConnection($connector, $remoteCerebrate, $requestData) { - $connectorResult = $connector->finaliseConnection($requestData['data']); + $connection = $this->getConnection($requestData); + $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); + $connectorResult = $connector->finaliseConnection($params); return $connectorResult; } } diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 70116d9..d1cc910 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -112,6 +112,10 @@ class AppController extends Controller $this->Security->setConfig('validatePost', false); } $this->Security->setConfig('unlockedActions', ['index']); + if ($this->ParamHandler->isRest()) { + $this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]); + $this->Security->setConfig('validatePost', false); + } $this->ACL->checkAccess(); $this->set('menu', $this->ACL->getMenu()); diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index c404c0e..f69995c 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -128,14 +128,7 @@ class InboxController extends AppController 'origin' => $this->request->clientIp(), 'user_id' => $this->ACL->getUser()['id'], ]; - $entryData['data'] = $this->request->data ?? []; - // $entryData['data'] = [ - // 'connectorName' => 'MispConnector', - // 'cerebrateURL' => 'http://localhost:8000', - // 'url' => 'https://localhost:8443', - // 'email' => 'admin@admin.test', - // 'authkey' => 'DkM9fEfwrG8Bg3U0ncKamocIutKt5YaUFuxzsB6b', - // ]; + $entryData['data'] = $this->request->getData() ?? []; $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); if ($scope == 'LocalTool') { $this->validateLocalToolRequestEntry($entryData); diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index a49e59f..835b8a1 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -229,7 +229,27 @@ class LocalToolsController extends AppController } $params['local_tool_id'] = $postParams['local_tool_id']; $encodingResult = $this->LocalTools->encodeConnection($params); - $this->redirect(); + $inboxResult = $encodingResult['inboxResult']; + if ($inboxResult['success']) { + if ($this->ParamHandler->isRest()) { + $response = $this->RestResponse->viewData($inboxResult, 'json'); + } else if ($this->ParamHandler->isAjax()) { + $response = $this->RestResponse->ajaxSuccessResponse('LocalTool', 'connectionRequest', [], $inboxResult['message']); + } else { + $this->Flash->success($inboxResult['message']); + $this->redirect(['action' => 'broodTools', $cerebrate_id]); + } + } else { + if ($this->ParamHandler->isRest()) { + $response = $this->RestResponse->viewData($inboxResult, 'json'); + } else if ($this->ParamHandler->isAjax()) { + $response = $this->RestResponse->ajaxFailResponse('LocalTool', 'connectionRequest', [], $inboxResult['message'], $inboxResult['errors']); + } else { + $this->Flash->error($inboxResult['message']); + $this->redirect($this->referer()); + } + } + return $response; } else { $remoteTool = $this->LocalTools->getRemoteToolById($params); $local_tools = $this->LocalTools->encodeConnectionChoice($params); diff --git a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php index acfc2e3..8999d6d 100644 --- a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php +++ b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php @@ -7,6 +7,7 @@ class CommonConnectorTools { public $description = ''; public $name = ''; + public $connectorName = ''; public $exposedFunctions = [ 'diagnostics' ]; diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 35c6ccc..1d3457c 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -11,6 +11,7 @@ class MispConnector extends CommonConnectorTools { public $description = 'MISP connector, handling diagnostics, organisation and sharing group management of your instance. Synchronisation requests can also be managed through the connector.'; + public $connectorName = 'MispConnector'; public $name = 'MISP'; public $exposedFunctions = [ @@ -100,16 +101,19 @@ class MispConnector extends CommonConnectorTools } } - public function getHTTPClient(Object $connection): Object + private function getHeaders(array $connectionSettings): array + { + return [ + 'AUTHORIZATION' => $connectionSettings['authkey'], + 'Accept' => 'application/json', + 'Content-type' => 'application/json' + ]; + } + + private function getHTTPClient(Object $connection): Object { $settings = json_decode($connection->settings, true); - $options = [ - 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ], - ]; + $options = []; if (!empty($settings['skip_ssl'])) { $options['ssl_verify_peer'] = false; $options['ssl_verify_host'] = false; @@ -126,7 +130,12 @@ class MispConnector extends CommonConnectorTools $http = $this->getHTTPClient($connection); try { - $response = $http->post($settings['url'] . '/users/view/me.json', '{}'); + $response = $http->post($settings['url'] . '/users/view/me.json', '{}', ['headers' => [ + 'AUTHORIZATION' => $settings['authkey'], + 'Accept' => 'application/json', + ], + 'type' => 'json', + ]); } catch (\Exception $e) { return [ 'status' => 0, @@ -171,7 +180,7 @@ class MispConnector extends CommonConnectorTools 'AUTHORIZATION' => $settings['authkey'], 'Accept' => 'application/json', 'Content-type' => 'application/json' - ] + ] ]); if ($response->isOk()) { return $response; @@ -193,15 +202,15 @@ class MispConnector extends CommonConnectorTools $url = $this->urlAppendParams($url, $params); $response = $http->post($settings['url'] . $url, json_encode($params['body']), [ 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'application/json' + 'AUTHORIZATION' => $settings['authkey'], + 'Accept' => 'application/json', ], - 'type' => 'json' + 'type' => 'json', ]); if ($response->isOk()) { return $response; } else { - throw new NotFoundException(__('Could not post to the requested resource.')); + throw new NotFoundException(__('Could not post to the requested resource. Remote returned:') . PHP_EOL . $response->getStringBody()); } } @@ -701,12 +710,14 @@ class MispConnector extends CommonConnectorTools $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'], + $serverParams = $params; + $serverParams['body'] = [ + 'authkey' => $params['remote_tool_data']['authkey'], + 'url' => $params['remote_tool_data']['url'], + 'name' => !empty($params['remote_tool_data']['name']) ? $params['remote_tool_data']['name'] : 'Empty name fix me', 'remote_org_id' => $params['misp_organisation']['id'] - ]); + ]; + $params['sync_connection'] = $this->addServer($serverParams); return [ 'email' => $params['sync_user']['email'], 'authkey' => $params['sync_user']['authkey'], @@ -716,12 +727,15 @@ class MispConnector extends CommonConnectorTools 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'], + $params['misp_organisation'] = $this->getSetOrg($params); + $serverParams = $params; + $serverParams['body'] = [ + 'authkey' => $params['remote_tool_data']['authkey'], + 'url' => $params['remote_tool_data']['url'], + 'name' => !empty($params['remote_tool_data']['name']) ? $params['remote_tool_data']['name'] : 'Empty name fix me', 'remote_org_id' => $params['misp_organisation']['id'] - ]); + ]; + $params['sync_connection'] = $this->addServer($serverParams); return true; } @@ -733,6 +747,7 @@ class MispConnector extends CommonConnectorTools $organisation = $response->getJson()['Organisation']; if (!$organisation['local']) { $organisation['local'] = 1; + $params['body'] = $organisation; $response = $this->postData('/admin/organisations/edit/' . $organisation['id'], $params); if (!$response->isOk()) { throw new MethodNotAllowedException(__('Could not update the organisation in MISP.')); @@ -771,10 +786,10 @@ class MispConnector extends CommonConnectorTools private function addServer(array $params): array { if ( - empty($params['authkey']) || - empty($params['url']) || - empty($params['remote_org_id']) || - empty($params['name']) + empty($params['body']['authkey']) || + empty($params['body']['url']) || + empty($params['body']['remote_org_id']) || + empty($params['body']['name']) ) { throw new MethodNotAllowedException(__('Required data missing from the sync connection object. The following fields are required: [name, url, authkey, org_id].')); } diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index 98a62ae..7113d62 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -5,7 +5,10 @@ namespace App\Model\Table; use App\Model\Table\AppTable; use Cake\ORM\Table; use Cake\Validation\Validator; +use Cake\Core\Configure; use Cake\Http\Client; +use Cake\Http\Client\Response; +use Cake\Http\Exception\NotFoundException; use Cake\ORM\TableRegistry; use Cake\Error\Debugger; @@ -192,4 +195,57 @@ class BroodsTable extends AppTable return false; } } + + public function sendRequest($brood, $urlPath, $methodPost = true, $data = []): Response + { + $http = new Client(); + $config = [ + 'headers' => [ + 'AUTHORIZATION' => $brood->authkey, + 'Accept' => 'application/json' + ], + 'type' => 'json' + ]; + $url = $brood->url . $urlPath; + if ($methodPost) { + $response = $http->post($url, json_encode($data), $config); + } else { + $response = $http->get($brood->url, $data, $config); + } + if ($response->isOk()) { + return $response; + } else { + throw new NotFoundException(__('Could not send to the requested resource.')); + } + } + + private function injectRequiredData($params, $data): Array + { + $data['connectorName'] = $params['remote_tool']['connector']; + $data['cerebrateURL'] = Configure::read('App.fullBaseUrl'); + $data['local_tool_id'] = $params['connection']['id']; + $data['remote_tool_id'] = $params['remote_tool']['id']; + return $data; + } + + public function sendLocalToolConnectionRequest($params, $data): Response + { + $url = '/inbox/createInboxEntry/LocalTool/IncomingConnectionRequest'; + $data = $this->injectRequiredData($params, $data); + return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); + } + + public function sendLocalToolAcceptedRequest($params, $data): Response + { + $url = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; + $data = $this->injectRequiredData($params, $data); + return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); + } + + public function sendLocalToolDeclinedRequest($params, $data): Response + { + $url = '/inbox/createInboxEntry/LocalTool/DeclinedRequest'; + $data = $this->injectRequiredData($params, $data); + return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); + } } diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php index 5b0d8bf..1640bc2 100644 --- a/src/Model/Table/InboxTable.php +++ b/src/Model/Table/InboxTable.php @@ -64,29 +64,6 @@ class InboxTable extends AppTable return $rules; } - public function sendRequest($brood, $urlPath, $methodPost = true, $data = []): boolean - { - $http = new Client(); - $config = [ - 'headers' => [ - 'AUTHORIZATION' => $brood->authkey, - 'Accept' => 'application/json' - ], - 'type' => 'json' - ]; - $url = $brood->url . $urlPath; - if ($methodPost) { - $response = $http->post($url, json_encode(data), $config); - } else { - $response = $http->get($brood->url, json_encode(data), $config); - } - if ($response->isOk()) { - return $response; - } else { - 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'); @@ -105,7 +82,7 @@ class InboxTable extends AppTable } } if (!$found) { - $errors[] = __('User is not part of the brood organisation'); + $errors[] = __('User `{0}` is not part of the brood\'s organisation. Make sure `{0}` is aligned with the organisation owning the brood.', $user->individual->email); } return $errors; } diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index ea701d5..159878a 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -205,9 +205,12 @@ class LocalToolsTable extends AppTable public function encodeConnection(array $params): array { $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; + $localResult = $params['connector'][$params['remote_tool']['connector']]->initiateConnectionWrapper($params); + $inboxResult = $this->sendEncodedConnection($params, $localResult); + return [ + 'inboxResult' => $inboxResult, + 'localResult' => $localResult + ]; } public function buildConnectionParams(array $params): array @@ -244,12 +247,29 @@ class LocalToolsTable extends AppTable return $local_tools; } - public function sendEncodedConnection($remoteCerebrate, $connectorName, $encodedConnection) + public function sendEncodedConnection($params, $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 + $this->Broods = \Cake\ORM\TableRegistry::getTableLocator()->get('Broods'); + try { + $response = $this->Broods->sendLocalToolConnectionRequest($params, $encodedConnection); + $jsonReply = $response->getJson(); + if (empty($jsonReply['success'])) { + $this->handleMessageNotCreated($response); + } + } catch (NotFoundException $e) { + return $this->handleSendingFailed($response); + } + return $jsonReply; + } + + public function handleSendingFailed($response) + { + // debug('sending failed. Modify state and add entry in outbox'); + throw new NotFoundException(__('sending failed. Modify state and add entry in outbox')); + } + + public function handleMessageNotCreated($response) + { + // debug('Saving message failed. Modify state and add entry in outbox'); } } diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index 8f6108c..de52527 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -997,13 +997,17 @@ class FormValidationHelper { } else { $messageNode.addClass('invalid-feedback') } - const hasMultipleErrors = Object.keys(errors).length > 1 - for (const [ruleName, error] of Object.entries(errors)) { - if (hasMultipleErrors) { - $messageNode.append($('
  • ').text(error)) - } else { - $messageNode.text(error) + if (typeof errors === 'object') { + const hasMultipleErrors = Object.keys(errors).length > 1 + for (const [ruleName, error] of Object.entries(errors)) { + if (hasMultipleErrors) { + $messageNode.append($('
  • ').text(error)) + } else { + $messageNode.text(error) + } } + } else { + $messageNode.text(errors) } return $messageNode } From 304a6ea1696ad60f09b430173f79c9271fbbb8aa Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 09:22:04 +0200 Subject: [PATCH 34/78] chg: [requestProcessor:genericRequest] Improved UI --- .../templates/LocalTool/GenericRequest.php | 2 +- src/View/Helper/BootstrapHelper.php | 2 +- webroot/css/bootstrap-additional.css | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php index df45df9..514dc6f 100644 --- a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -109,7 +109,7 @@ $requestData = $this->Bootstrap->collapse( 'title' => __('Inter-connection data'), 'open' => true, ], - sprintf('
    %s
    ', json_encode($request['data'], JSON_PRETTY_PRINT)) + sprintf('
    %s
    ', json_encode($request['data'], JSON_PRETTY_PRINT)) ); $bodyHtml = sprintf('
    %s
    %s
    %s
    ', diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index d0562f1..22895fa 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -1142,7 +1142,7 @@ class BoostrapProgressTimeline extends BootstrapGeneric { ], ], $textLis); $html = $this->genNode('div', [ - 'class' => ['progress-timeline'] + 'class' => ['progress-timeline', 'mw-75', 'mx-auto'] ], $ulIcons . $ulText); return $html; } diff --git a/webroot/css/bootstrap-additional.css b/webroot/css/bootstrap-additional.css index e25fd7c..95e2e80 100644 --- a/webroot/css/bootstrap-additional.css +++ b/webroot/css/bootstrap-additional.css @@ -1,3 +1,23 @@ +/* utils */ +.mw-75 { + max-width: 75% !important; + } +.mw-50 { + max-width: 50% !important; +} +.mw-25 { + max-width: 25% !important; +} +.mh-75 { + max-width: 75% !important; +} +.mh-50 { + max-width: 50% !important; +} +.mh-25 { + max-width: 25% !important; +} + /* Toast */ .toast { min-width: 250px; From 49a4c37eb6f04394697d8ceff82bedbff5ef47ea Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 10:03:03 +0200 Subject: [PATCH 35/78] chg: [requestProcessor:localTool] Extendable view with specific local tool action templates --- .../GenericRequestProcessor.php | 3 ++- .../LocalToolRequestProcessor.php | 3 ++- .../templates/LocalTool/GenericRequest.php | 13 ++++-------- .../IncomingConnectionRequest.php | 21 +++++++++++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/RequestProcessors/GenericRequestProcessor.php index 1bfd974..dc07fff 100644 --- a/libraries/default/RequestProcessors/GenericRequestProcessor.php +++ b/libraries/default/RequestProcessors/GenericRequestProcessor.php @@ -66,8 +66,9 @@ class GenericRequestProcessor public function render($request=[], Cake\Http\ServerRequest $serverRequest) { - $processingTemplate = $this->getProcessingTemplate(); $viewVariables = $this->getViewVariables($request); + $this->updateProcessingTemplate($request); + $processingTemplate = $this->getProcessingTemplate(); $builder = new ViewBuilder(); $builder->disableAutoLayout() ->setClassName('Monad') diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index e5160bf..315c574 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -31,8 +31,9 @@ class LocalToolRequestProcessor extends GenericRequestProcessor return parent::create($requestData); } - protected function assignProcessingTemplate($connectorName) + protected function updateProcessingTemplate($request) { + $connectorName = $request->connector['connector']; $processingTemplatePath = sprintf('%s/%s/%s.php', $this->scope, $connectorName, $this->action); $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); if ($file->exists()) { diff --git a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php index 514dc6f..093cd80 100644 --- a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php @@ -91,18 +91,13 @@ $form = $this->element('genericElements/Form/genericForm', [ 'raw' => true, 'data' => [ 'model' => 'Inbox', - 'fields' => [ - [ - 'field' => 'is_discard', - 'type' => 'checkbox', - 'default' => false - ] - ], + 'fields' => [], 'submit' => [ 'action' => $this->request->getParam('action') ] ] ]); +$localToolHTML = $this->fetch('content', sprintf('
    %s
    ', $form)); $requestData = $this->Bootstrap->collapse( [ @@ -112,10 +107,10 @@ $requestData = $this->Bootstrap->collapse( sprintf('
    %s
    ', json_encode($request['data'], JSON_PRETTY_PRINT)) ); -$bodyHtml = sprintf('
    %s
    %s
    %s
    ', +$bodyHtml = sprintf('
    %s
    %s
    %s', $table, $requestData, - $form + $localToolHTML ); echo $this->Bootstrap->modal([ diff --git a/libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php b/libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php new file mode 100644 index 0000000..904d39d --- /dev/null +++ b/libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php @@ -0,0 +1,21 @@ +extend('LocalTool/GenericRequest'); +$form = $this->element('genericElements/Form/genericForm', [ + 'entity' => null, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'model' => 'Inbox', + 'fields' => [ + [ + 'field' => 'is_discard', + 'type' => 'checkbox', + 'default' => false + ] + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] +]); +echo sprintf('
    %s
    ', $form); \ No newline at end of file From ae423bfb68f18983361f8638a665ff1f4d47b47d Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 10:17:02 +0200 Subject: [PATCH 36/78] chg: [brood] Moved request sender handler in the brood table --- src/Model/Table/BroodsTable.php | 36 +++++++++++++++++++++++++++-- src/Model/Table/LocalToolsTable.php | 21 +---------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index 7113d62..e1dbd27 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -228,11 +228,20 @@ class BroodsTable extends AppTable return $data; } - public function sendLocalToolConnectionRequest($params, $data): Response + public function sendLocalToolConnectionRequest($params, $data): array { $url = '/inbox/createInboxEntry/LocalTool/IncomingConnectionRequest'; $data = $this->injectRequiredData($params, $data); - return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); + $response = $this->sendRequest($params['remote_cerebrate'], $url, true, $data); + try { + $jsonReply = $response->getJson(); + if (empty($jsonReply['success'])) { + $this->handleMessageNotCreated($response); + } + } catch (NotFoundException $e) { + $jsonReply = $this->handleSendingFailed($response); + } + return $jsonReply; } public function sendLocalToolAcceptedRequest($params, $data): Response @@ -248,4 +257,27 @@ class BroodsTable extends AppTable $data = $this->injectRequiredData($params, $data); return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); } + + /** + * handleSendingFailed - Handle the case if the request could not be sent or if the remote rejected the connection request + * + * @param Object $response + * @return array + */ + private function handleSendingFailed(Object $response): array + { + // debug('sending failed. Modify state and add entry in outbox'); + throw new NotFoundException(__('sending failed. Modify state and add entry in outbox')); + } + + /** + * handleMessageNotCreated - Handle the case if the request was sent but the remote brood did not save the message in the inbox + * + * @param Object $response + * @return array + */ + private function handleMessageNotCreated(Object $response): array + { + // debug('Saving message failed. Modify state and add entry in outbox'); + } } diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 159878a..876cda5 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -250,26 +250,7 @@ class LocalToolsTable extends AppTable public function sendEncodedConnection($params, $encodedConnection) { $this->Broods = \Cake\ORM\TableRegistry::getTableLocator()->get('Broods'); - try { - $response = $this->Broods->sendLocalToolConnectionRequest($params, $encodedConnection); - $jsonReply = $response->getJson(); - if (empty($jsonReply['success'])) { - $this->handleMessageNotCreated($response); - } - } catch (NotFoundException $e) { - return $this->handleSendingFailed($response); - } + $jsonReply = $this->Broods->sendLocalToolConnectionRequest($params, $encodedConnection); return $jsonReply; } - - public function handleSendingFailed($response) - { - // debug('sending failed. Modify state and add entry in outbox'); - throw new NotFoundException(__('sending failed. Modify state and add entry in outbox')); - } - - public function handleMessageNotCreated($response) - { - // debug('Saving message failed. Modify state and add entry in outbox'); - } } From 3bf7f5a3e4f25b7fef6de20ce3267ba8379303ad Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 18 Jun 2021 10:46:54 +0200 Subject: [PATCH 37/78] fix: [add encryption key] view description fixed, fixes #29 --- templates/EncryptionKeys/add.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/EncryptionKeys/add.php b/templates/EncryptionKeys/add.php index 3f1800e..a0b00dd 100644 --- a/templates/EncryptionKeys/add.php +++ b/templates/EncryptionKeys/add.php @@ -2,7 +2,7 @@ echo $this->element('genericElements/Form/genericForm', [ 'data' => [ 'title' => __('Add new encryption key'), - 'description' => __('Alignments indicate that an individual belongs to an organisation in one way or another. The type of relationship is defined by the type field.'), + 'description' => __('Assign encryption keys to the user, used to securely communicate or validate messages coming from the user.'), 'model' => 'Organisations', 'fields' => [ [ From 4080654806765f6919c3f9b6a31477b9200f4521 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 11:09:19 +0200 Subject: [PATCH 38/78] chg: [inboxProcessor] Renamed `RequestProcessors` into `InboxProcessors` --- .../BroodRequestProcessor.php | 6 +-- .../GenericRequestProcessor.php | 8 +-- .../LocalToolRequestProcessor.php | 10 ++-- .../ProposalRequestProcessor.php | 6 +-- .../SynchronisationRequestProcessor.php | 6 +-- .../TemplateRequestProcessor.php.template | 6 +-- .../UserRequestProcessor.php | 6 +-- src/Controller/BroodsController.php | 4 +- src/Controller/InboxController.php | 28 +++++----- src/Controller/UsersController.php | 4 +- ...ssorTable.php => InboxProcessorsTable.php} | 54 +++++++++---------- src/View/MonadView.php | 2 +- 12 files changed, 70 insertions(+), 70 deletions(-) rename src/Model/Table/{RequestProcessorTable.php => InboxProcessorsTable.php} (64%) diff --git a/libraries/default/RequestProcessors/BroodRequestProcessor.php b/libraries/default/RequestProcessors/BroodRequestProcessor.php index 0ea01bb..f055c1e 100644 --- a/libraries/default/RequestProcessors/BroodRequestProcessor.php +++ b/libraries/default/RequestProcessors/BroodRequestProcessor.php @@ -1,9 +1,9 @@ Inbox = TableRegistry::getTableLocator()->get('Inbox'); @@ -150,7 +150,7 @@ class GenericRequestProcessor if ($controller->ParamHandler->isRest()) { $response = $controller->RestResponse->viewData($processResult, 'json'); } else if ($controller->ParamHandler->isAjax()) { - $response = $controller->RestResponse->ajaxSuccessResponse('RequestProcessor', "{$scope}.{$action}", $processResult['data'], $message); + $response = $controller->RestResponse->ajaxSuccessResponse('InboxProcessor', "{$scope}.{$action}", $processResult['data'], $message); } else { $controller->Flash->success($message); if (!is_null($redirect)) { @@ -164,7 +164,7 @@ class GenericRequestProcessor if ($controller->ParamHandler->isRest()) { $response = $controller->RestResponse->viewData($processResult, 'json'); } else if ($controller->ParamHandler->isAjax()) { - $response = $controller->RestResponse->ajaxFailResponse('RequestProcessor', "{$scope}.{$action}", $processResult['data'], $message, $processResult['errors']); + $response = $controller->RestResponse->ajaxFailResponse('InboxProcessor', "{$scope}.{$action}", $processResult['data'], $message, $processResult['errors']); } else { $controller->Flash->error($message); if (!is_null($redirect)) { diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php index 315c574..5bddbb0 100644 --- a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php +++ b/libraries/default/RequestProcessors/LocalToolRequestProcessor.php @@ -3,9 +3,9 @@ use Cake\ORM\TableRegistry; use Cake\Filesystem\File; use Cake\Http\Exception\NotFoundException; -require_once(ROOT . DS . 'libraries' . DS . 'default' . DS . 'RequestProcessors' . DS . 'GenericRequestProcessor.php'); +require_once(ROOT . DS . 'libraries' . DS . 'default' . DS . 'InboxProcessors' . DS . 'GenericInboxProcessor.php'); -class LocalToolRequestProcessor extends GenericRequestProcessor +class LocalToolInboxProcessor extends GenericInboxProcessor { protected $scope = 'LocalTool'; protected $action = 'not-specified'; //overriden when extending @@ -171,7 +171,7 @@ class LocalToolRequestProcessor extends GenericRequestProcessor } } -class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { +class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { public $action = 'IncomingConnectionRequest'; protected $description; @@ -283,7 +283,7 @@ class IncomingConnectionRequestProcessor extends LocalToolRequestProcessor imple } } -class AcceptedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { +class AcceptedRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { public $action = 'AcceptedRequest'; protected $description; @@ -356,7 +356,7 @@ class AcceptedRequestProcessor extends LocalToolRequestProcessor implements Gene } } -class DeclinedRequestProcessor extends LocalToolRequestProcessor implements GenericProcessorActionI { +class DeclinedRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { public $action = 'DeclinedRequest'; protected $description; diff --git a/libraries/default/RequestProcessors/ProposalRequestProcessor.php b/libraries/default/RequestProcessors/ProposalRequestProcessor.php index 7eab1c8..baec684 100644 --- a/libraries/default/RequestProcessors/ProposalRequestProcessor.php +++ b/libraries/default/RequestProcessors/ProposalRequestProcessor.php @@ -1,9 +1,9 @@ requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); - $processor = $this->requestProcessor->getProcessor('Brood', 'ToolInterconnection'); + $this->InboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $processor = $this->InboxProcessors->getProcessor('Brood', 'ToolInterconnection'); $data = [ 'origin' => '127.0.0.1', 'comment' => 'Test comment', diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index f69995c..a393ad3 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -61,8 +61,8 @@ class InboxController extends AppController { if ($this->request->is('post')) { $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); - $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); - $processor = $this->requestProcessor->getProcessor($request->scope, $request->action); + $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $processor = $this->inboxProcessors->getProcessor($request->scope, $request->action); $discardResult = $processor->discard($id, $request); return $processor->genHTTPReply($this, $discardResult); } @@ -81,11 +81,11 @@ class InboxController extends AppController $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); $scope = $request->scope; $action = $request->action; - $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); if ($scope == 'LocalTool') { - $processor = $this->requestProcessor->getLocalToolProcessor($action, $request->local_tool_name); + $processor = $this->inboxProcessors->getLocalToolProcessor($action, $request->local_tool_name); } else { - $processor = $this->requestProcessor->getProcessor($scope, $action); + $processor = $this->inboxProcessors->getProcessor($scope, $action); } if ($this->request->is('post')) { $processResult = $processor->process($id, $this->request->getData(), $request); @@ -98,14 +98,14 @@ class InboxController extends AppController public function listProcessors() { - $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); - $requestProcessors = $this->requestProcessor->listProcessors(); + $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $processors = $this->inboxProcessors->listProcessors(); if ($this->ParamHandler->isRest()) { - return $this->RestResponse->viewData($requestProcessors, 'json'); + return $this->RestResponse->viewData($processors, 'json'); } $data = []; - foreach ($requestProcessors as $scope => $processors) { - foreach ($processors as $processor) { + foreach ($processors as $scope => $scopedProcessors) { + foreach ($scopedProcessors as $processor) { $data[] = [ 'enabled' => $processor->enabled, 'scope' => $scope, @@ -129,20 +129,20 @@ class InboxController extends AppController 'user_id' => $this->ACL->getUser()['id'], ]; $entryData['data'] = $this->request->getData() ?? []; - $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); if ($scope == 'LocalTool') { $this->validateLocalToolRequestEntry($entryData); $entryData['origin'] = $entryData['data']['cerebrateURL']; - $processor = $this->requestProcessor->getLocalToolProcessor($action, $entryData['data']['connectorName']); + $processor = $this->inboxProcessors->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); } } else { - $processor = $this->requestProcessor->getProcessor($scope, $action); + $processor = $this->inboxProcessors->getProcessor($scope, $action); } - $creationResult = $this->requestProcessor->createInboxEntry($processor, $entryData); + $creationResult = $this->inboxProcessors->createInboxEntry($processor, $entryData); return $processor->genHTTPReply($this, $creationResult); } diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 8a40151..9126e87 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -138,8 +138,8 @@ class UsersController extends AppController public function register() { - $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); - $processor = $this->requestProcessor->getProcessor('User', 'Registration'); + $this->InboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $processor = $this->InboxProcessors->getProcessor('User', 'Registration'); $data = [ 'origin' => '127.0.0.1', 'comment' => 'Hi there!, please create an account', diff --git a/src/Model/Table/RequestProcessorTable.php b/src/Model/Table/InboxProcessorsTable.php similarity index 64% rename from src/Model/Table/RequestProcessorTable.php rename to src/Model/Table/InboxProcessorsTable.php index e18433d..0776f25 100644 --- a/src/Model/Table/RequestProcessorTable.php +++ b/src/Model/Table/InboxProcessorsTable.php @@ -7,15 +7,15 @@ use Cake\Filesystem\Folder; use Cake\Http\Exception\MethodNotAllowedException; use Cake\Core\Exception\Exception; -class MissingRequestProcessorException extends Exception +class MissingInboxProcessorException extends Exception { protected $_defaultCode = 404; } -class RequestProcessorTable extends AppTable +class InboxProcessors extends AppTable { - private $processorsDirectory = ROOT . '/libraries/default/RequestProcessors'; - private $requestProcessors; + private $processorsDirectory = ROOT . '/libraries/default/InboxProcessors'; + private $inboxProcessors; private $enabledProcessors = [ // to be defined in config 'Brood' => [ 'ToolInterconnection' => false, @@ -40,16 +40,16 @@ class RequestProcessorTable extends AppTable public function getProcessor($scope, $action=null) { - if (isset($this->requestProcessors[$scope])) { + if (isset($this->inboxProcessors[$scope])) { if (is_null($action)) { - return $this->requestProcessors[$scope]; - } else if (!empty($this->requestProcessors[$scope]->{$action})) { - return $this->requestProcessors[$scope]->{$action}; + return $this->inboxProcessors[$scope]; + } else if (!empty($this->inboxProcessors[$scope]->{$action})) { + return $this->inboxProcessors[$scope]->{$action}; } else { throw new \Exception(__('Processor {0}.{1} not found', $scope, $action)); } } - throw new MissingRequestProcessorException(__('Processor not found')); + throw new MissingInboxProcessorException(__('Processor not found')); } public function getLocalToolProcessor($action, $connectorName) @@ -58,7 +58,7 @@ class RequestProcessorTable extends AppTable $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) { + } catch (MissingInboxProcessorException $e) { $processor = $this->getProcessor($scope, $action); } return $processor; @@ -67,12 +67,12 @@ class RequestProcessorTable extends AppTable public function listProcessors($scope=null) { if (is_null($scope)) { - return $this->requestProcessors; + return $this->inboxProcessors; } else { - if (isset($this->requestProcessors[$scope])) { - return $this->requestProcessors[$scope]; + if (isset($this->inboxProcessors[$scope])) { + return $this->inboxProcessors[$scope]; } else { - throw new MissingRequestProcessorException(__('Processors for {0} not found', $scope)); + throw new MissingInboxProcessorException(__('Processors for {0} not found', $scope)); } } } @@ -80,30 +80,30 @@ class RequestProcessorTable extends AppTable private function loadProcessors() { $processorDir = new Folder($this->processorsDirectory); - $processorFiles = $processorDir->find('.*RequestProcessor\.php', true); + $processorFiles = $processorDir->find('.*InboxProcessor\.php', true); foreach ($processorFiles as $processorFile) { - if ($processorFile == 'GenericRequestProcessor.php') { + if ($processorFile == 'GenericInboxProcessor.php') { continue; } $processorMainClassName = str_replace('.php', '', $processorFile); - $processorMainClassNameShort = str_replace('RequestProcessor.php', '', $processorFile); + $processorMainClassNameShort = str_replace('InboxProcessor.php', '', $processorFile); $processorMainClass = $this->getProcessorClass($processorDir->pwd() . DS . $processorFile, $processorMainClassName); if (is_object($processorMainClass)) { - $this->requestProcessors[$processorMainClassNameShort] = $processorMainClass; - foreach ($this->requestProcessors[$processorMainClassNameShort]->getRegisteredActions() as $registeredAction) { - $scope = $this->requestProcessors[$processorMainClassNameShort]->getScope(); + $this->inboxProcessors[$processorMainClassNameShort] = $processorMainClass; + foreach ($this->inboxProcessors[$processorMainClassNameShort]->getRegisteredActions() as $registeredAction) { + $scope = $this->inboxProcessors[$processorMainClassNameShort]->getScope(); if (!empty($this->enabledProcessors[$scope][$registeredAction])) { - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = true; + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = true; } else { - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; } } } else { - $this->requestProcessors[$processorMainClassNameShort] = new \stdClass(); - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction} = new \stdClass(); - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->action = "N/A"; - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; - $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->error = $processorMainClass; + $this->inboxProcessors[$processorMainClassNameShort] = new \stdClass(); + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction} = new \stdClass(); + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction}->action = "N/A"; + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + $this->inboxProcessors[$processorMainClassNameShort]->{$registeredAction}->error = $processorMainClass; } } } diff --git a/src/View/MonadView.php b/src/View/MonadView.php index 6c72ef3..f0806a0 100644 --- a/src/View/MonadView.php +++ b/src/View/MonadView.php @@ -22,7 +22,7 @@ namespace App\View; class MonadView extends AppView { private $additionalTemplatePaths = [ - ROOT . '/libraries/default/RequestProcessors/templates/', + ROOT . '/libraries/default/InboxProcessors/templates/', ]; protected function _paths(?string $plugin = null, bool $cached = true): array From afa2b2872de8f31f1b346dbe2d37243b1f872ff5 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 11:36:00 +0200 Subject: [PATCH 39/78] chg: [inboxProcessor] Renamed processors to use `inbox` instead of `request` --- .../BroodInboxProcessor.php} | 0 .../GenericInboxProcessor.php} | 0 .../LocalToolInboxProcessor.php} | 0 .../ProposalInboxProcessor.php} | 0 .../SynchronisationInboxProcessor.php} | 0 .../TemplateInboxProcessor.php.template} | 0 .../UserInboxProcessor.php} | 0 .../templates/LocalTool/GenericRequest.php | 0 .../LocalTool/MispConnector/IncomingConnectionRequest.php | 0 .../templates/User/Registration.php | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename libraries/default/{RequestProcessors/BroodRequestProcessor.php => InboxProcessors/BroodInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors/GenericRequestProcessor.php => InboxProcessors/GenericInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors/LocalToolRequestProcessor.php => InboxProcessors/LocalToolInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors/ProposalRequestProcessor.php => InboxProcessors/ProposalInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors/SynchronisationRequestProcessor.php => InboxProcessors/SynchronisationInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors/TemplateRequestProcessor.php.template => InboxProcessors/TemplateInboxProcessor.php.template} (100%) rename libraries/default/{RequestProcessors/UserRequestProcessor.php => InboxProcessors/UserInboxProcessor.php} (100%) rename libraries/default/{RequestProcessors => InboxProcessors}/templates/LocalTool/GenericRequest.php (100%) rename libraries/default/{RequestProcessors => InboxProcessors}/templates/LocalTool/MispConnector/IncomingConnectionRequest.php (100%) rename libraries/default/{RequestProcessors => InboxProcessors}/templates/User/Registration.php (100%) diff --git a/libraries/default/RequestProcessors/BroodRequestProcessor.php b/libraries/default/InboxProcessors/BroodInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/BroodRequestProcessor.php rename to libraries/default/InboxProcessors/BroodInboxProcessor.php diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/InboxProcessors/GenericInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/GenericRequestProcessor.php rename to libraries/default/InboxProcessors/GenericInboxProcessor.php diff --git a/libraries/default/RequestProcessors/LocalToolRequestProcessor.php b/libraries/default/InboxProcessors/LocalToolInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/LocalToolRequestProcessor.php rename to libraries/default/InboxProcessors/LocalToolInboxProcessor.php diff --git a/libraries/default/RequestProcessors/ProposalRequestProcessor.php b/libraries/default/InboxProcessors/ProposalInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/ProposalRequestProcessor.php rename to libraries/default/InboxProcessors/ProposalInboxProcessor.php diff --git a/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php b/libraries/default/InboxProcessors/SynchronisationInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/SynchronisationRequestProcessor.php rename to libraries/default/InboxProcessors/SynchronisationInboxProcessor.php diff --git a/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template b/libraries/default/InboxProcessors/TemplateInboxProcessor.php.template similarity index 100% rename from libraries/default/RequestProcessors/TemplateRequestProcessor.php.template rename to libraries/default/InboxProcessors/TemplateInboxProcessor.php.template diff --git a/libraries/default/RequestProcessors/UserRequestProcessor.php b/libraries/default/InboxProcessors/UserInboxProcessor.php similarity index 100% rename from libraries/default/RequestProcessors/UserRequestProcessor.php rename to libraries/default/InboxProcessors/UserInboxProcessor.php diff --git a/libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php similarity index 100% rename from libraries/default/RequestProcessors/templates/LocalTool/GenericRequest.php rename to libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php diff --git a/libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php b/libraries/default/InboxProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php similarity index 100% rename from libraries/default/RequestProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php rename to libraries/default/InboxProcessors/templates/LocalTool/MispConnector/IncomingConnectionRequest.php diff --git a/libraries/default/RequestProcessors/templates/User/Registration.php b/libraries/default/InboxProcessors/templates/User/Registration.php similarity index 100% rename from libraries/default/RequestProcessors/templates/User/Registration.php rename to libraries/default/InboxProcessors/templates/User/Registration.php From 063575d8b3f54cc58e1ccb8eda50113b612ebf3d Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 18 Jun 2021 11:59:25 +0200 Subject: [PATCH 40/78] fix: [inbox] Missing `Table` in class name --- libraries/default/InboxProcessors/GenericInboxProcessor.php | 4 ++++ src/Controller/InboxController.php | 6 +++--- src/Model/Table/InboxProcessorsTable.php | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/default/InboxProcessors/GenericInboxProcessor.php b/libraries/default/InboxProcessors/GenericInboxProcessor.php index 945aa57..f78a066 100644 --- a/libraries/default/InboxProcessors/GenericInboxProcessor.php +++ b/libraries/default/InboxProcessors/GenericInboxProcessor.php @@ -38,6 +38,10 @@ class GenericInboxProcessor $file->close(); } + protected function updateProcessingTemplate($request) + { + } + public function getRegisteredActions() { return $this->registeredActions; diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index a393ad3..b71e2d5 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -81,11 +81,11 @@ class InboxController extends AppController $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); $scope = $request->scope; $action = $request->action; - $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $this->InboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); if ($scope == 'LocalTool') { - $processor = $this->inboxProcessors->getLocalToolProcessor($action, $request->local_tool_name); + $processor = $this->InboxProcessors->getLocalToolProcessor($action, $request->local_tool_name); } else { - $processor = $this->inboxProcessors->getProcessor($scope, $action); + $processor = $this->InboxProcessors->getProcessor($scope, $action); } if ($this->request->is('post')) { $processResult = $processor->process($id, $this->request->getData(), $request); diff --git a/src/Model/Table/InboxProcessorsTable.php b/src/Model/Table/InboxProcessorsTable.php index 0776f25..4bb1aea 100644 --- a/src/Model/Table/InboxProcessorsTable.php +++ b/src/Model/Table/InboxProcessorsTable.php @@ -12,7 +12,7 @@ class MissingInboxProcessorException extends Exception protected $_defaultCode = 404; } -class InboxProcessors extends AppTable +class InboxProcessorsTable extends AppTable { private $processorsDirectory = ROOT . '/libraries/default/InboxProcessors'; private $inboxProcessors; From 1da74b283a17b2728e392f3b979d29d1f41084da Mon Sep 17 00:00:00 2001 From: mokaddem Date: Sat, 19 Jun 2021 13:16:25 +0200 Subject: [PATCH 41/78] new: [outbox] Added outbox and linked it with failed outgoing messages --- .../20210618102027_OutboxSystem.php | 90 ++++++++ .../InboxProcessors/GenericInboxProcessor.php | 2 +- .../BroodsOutboxProcessor.php | 107 +++++++++ .../GenericOutboxProcessor.php | 218 ++++++++++++++++++ .../TemplateOutoxProcessor.php.template | 65 ++++++ .../templates/Broods/ResendFailedMessage.php | 138 +++++++++++ src/Controller/Component/ACLComponent.php | 39 +++- src/Controller/InboxController.php | 4 +- src/Controller/LocalToolsController.php | 4 +- src/Controller/OutboxController.php | 125 ++++++++++ src/Model/Table/BroodsTable.php | 72 ++++-- src/Model/Table/InboxTable.php | 6 + src/Model/Table/OutboxProcessorsTable.php | 134 +++++++++++ src/Model/Table/OutboxTable.php | 70 ++++++ src/View/MonadView.php | 1 + templates/Outbox/index.php | 94 ++++++++ templates/Outbox/list_processors.php | 54 +++++ templates/Outbox/view.php | 47 ++++ 18 files changed, 1246 insertions(+), 24 deletions(-) create mode 100644 config/Migrations/20210618102027_OutboxSystem.php create mode 100644 libraries/default/OutboxProcessors/BroodsOutboxProcessor.php create mode 100644 libraries/default/OutboxProcessors/GenericOutboxProcessor.php create mode 100644 libraries/default/OutboxProcessors/TemplateOutoxProcessor.php.template create mode 100644 libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php create mode 100644 src/Controller/OutboxController.php create mode 100644 src/Model/Table/OutboxProcessorsTable.php create mode 100644 src/Model/Table/OutboxTable.php create mode 100644 templates/Outbox/index.php create mode 100644 templates/Outbox/list_processors.php create mode 100644 templates/Outbox/view.php diff --git a/config/Migrations/20210618102027_OutboxSystem.php b/config/Migrations/20210618102027_OutboxSystem.php new file mode 100644 index 0000000..2b74b71 --- /dev/null +++ b/config/Migrations/20210618102027_OutboxSystem.php @@ -0,0 +1,90 @@ +table('outbox', [ + 'signed' => false, + 'collation' => 'utf8mb4_unicode_ci', + ]); + $table + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'limit' => 10, + 'signed' => false, + ]) + ->addPrimaryKey('id') + ->addColumn('uuid', 'uuid', [ + 'default' => null, + 'null' => false, + ]) + ->addColumn('scope', 'string', [ + 'default' => null, + 'null' => false, + 'limit' => 191, + 'comment' => 'The to model linked with the message', + ]) + ->addColumn('action', 'string', [ + 'default' => null, + 'null' => false, + 'limit' => 191, + 'comment' => 'The action linked with the message', + ]) + ->addColumn('title', 'string', [ + 'default' => null, + 'null' => false, + 'limit' => 191, + ]) + ->addColumn('user_id', 'integer', [ + 'default' => null, + 'null' => true, + 'signed' => false, + 'length' => 10, + ]) + ->addColumn('comment', 'text', [ + 'default' => null, + 'null' => true, + ]) + ->addColumn('description', 'text', [ + 'default' => null, + 'null' => true, + ]) + ->addColumn('data', 'text', [ + 'default' => null, + 'null' => true, + 'limit' => MysqlAdapter::TEXT_LONG + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + + $table->addForeignKey('user_id', 'users', 'id', ['delete'=> 'CASCADE', 'update'=> 'CASCADE']); + + $table->addIndex(['uuid'], ['unique' => true]) + ->addIndex('scope') + ->addIndex('action') + ->addIndex('title') + ->addIndex('created') + ->addIndex('user_id'); + + $table->create(); + } +} + diff --git a/libraries/default/InboxProcessors/GenericInboxProcessor.php b/libraries/default/InboxProcessors/GenericInboxProcessor.php index f78a066..ea71884 100644 --- a/libraries/default/InboxProcessors/GenericInboxProcessor.php +++ b/libraries/default/InboxProcessors/GenericInboxProcessor.php @@ -193,7 +193,7 @@ class GenericInboxProcessor $requestData['action'] = $this->action; $requestData['description'] = $this->description; $request = $this->generateRequest($requestData); - $savedRequest = $this->Inbox->save($request); + $savedRequest = $this->Inbox->createEntry($request); return $this->genActionResult( $savedRequest, $savedRequest !== false, diff --git a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php new file mode 100644 index 0000000..a88ec1b --- /dev/null +++ b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php @@ -0,0 +1,107 @@ +Broods->find() + ->where(['id' => $broodId]) + ->first(); + return $brood; + } + + protected function getLocalTool($toolId) + { + $tool = $this->LocalTools->find() + ->where(['id' => $toolId]) + ->first(); + return $tool; + } +} + +class ResendFailedMessageProcessor extends BroodsOutboxProcessor implements GenericProcessorActionI { + public $action = 'ResendFailedMessage'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle re-sending messages that failed to be received from other cerebrate instances.'); + $this->Broods = TableRegistry::getTableLocator()->get('Broods'); + $this->LocalTools = \Cake\ORM\TableRegistry::getTableLocator()->get('LocalTools'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function getViewVariables($request) + { + $request->brood = $this->getIssuerBrood($request['data']['brood_id']); + $request->individual = $request->user->individual; + $request->localTool = $this->getLocalTool($request['data']['local_tool_id']); + $request->remoteTool = $request['data']['remote_tool']; + return [ + 'request' => $request, + ]; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $brood = $requestData['brood']; + $requestData['title'] = __('Issue while sending message to Cerebrate instance `{0}` using `{1}`', $brood->name, sprintf('%s.%s', $requestData['model'], $requestData['action'])); + return parent::create($requestData); + } + + public function process($id, $requestData, $outboxRequest) + { + if (!empty($requestData['is_delete'])) { // -> declined + $success = true; + $messageSucess = __('Message successfully deleted'); + $messageFail = ''; + } else { + $brood = $this->getIssuerBrood((int) $outboxRequest->data['brood_id']); + $url = $outboxRequest->data['url']; + $dataSent = $outboxRequest->data['sent']; + $dataSent['connectorName'] = 'MispConnector'; + $response = $this->Broods->sendRequest($brood, $url, true, $dataSent); + $jsonReply = $response->getJson(); + $success = !empty($jsonReply['success']); + $messageSuccess = __('Message successfully sent to `{0}`', $brood->name); + $messageFail = __('Could not send message to `{0}`.', $brood->name); + } + if ($success) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + [], + $success, + $success ? $messageSuccess : $messageFail, + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} diff --git a/libraries/default/OutboxProcessors/GenericOutboxProcessor.php b/libraries/default/OutboxProcessors/GenericOutboxProcessor.php new file mode 100644 index 0000000..eb97d28 --- /dev/null +++ b/libraries/default/OutboxProcessors/GenericOutboxProcessor.php @@ -0,0 +1,218 @@ +Outbox = TableRegistry::getTableLocator()->get('Outbox'); + if ($registerActions) { + $this->registerActionInProcessor(); + } + $this->assignProcessingTemplate(); + } + + private function assignProcessingTemplate() + { + $processingTemplatePath = $this->getProcessingTemplatePath(); + $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); + if ($file->exists()) { + $this->processingTemplate = str_replace('.php', '', $processingTemplatePath); + } + $file->close(); + } + + protected function updateProcessingTemplate($request) + { + } + + public function getRegisteredActions() + { + return $this->registeredActions; + } + public function getScope() + { + return $this->scope; + } + public function getDescription() + { + return $this->description ?? ''; + } + + protected function getProcessingTemplatePath() + { + return sprintf('%s/%s.php', + $this->scope, + $this->action + ); + } + + public function getProcessingTemplate() + { + return $this->processingTemplate; + } + + public function render($request=[], Cake\Http\ServerRequest $serverRequest) + { + $viewVariables = $this->getViewVariables($request); + $this->updateProcessingTemplate($request); + $processingTemplate = $this->getProcessingTemplate(); + $builder = new ViewBuilder(); + $builder->disableAutoLayout() + ->setClassName('Monad') + ->setTemplate($processingTemplate); + $view = $builder->build($viewVariables); + $view->setRequest($serverRequest); + return $view->render(); + } + + protected function generateRequest($requestData) + { + $request = $this->Outbox->newEmptyEntity(); + $request = $this->Outbox->patchEntity($request, $requestData); + if ($request->getErrors()) { + throw new Exception(__('Could not create request.{0}Reason: {1}', PHP_EOL, json_encode($request->getErrors())), 1); + } + return $request; + } + + protected function validateRequestData($requestData) + { + $errors = []; + if (!isset($requestData['data'])) { + $errors[] = __('No request data provided'); + } + $validator = new Validator(); + if (method_exists($this, 'addValidatorRules')) { + $validator = $this->addValidatorRules($validator); + $errors = $validator->validate($requestData['data']); + } + if (!empty($errors)) { + throw new Exception('Error while validating request data. ' . json_encode($errors), 1); + } + } + + protected function registerActionInProcessor() + { + foreach ($this->registeredActions as $i => $action) { + $className = "{$action}Processor"; + $reflection = new ReflectionClass($className); + if ($reflection->isAbstract() || $reflection->isInterface()) { + throw new Exception(__('Cannot create instance of %s, as it is abstract or is an interface')); + } + $this->{$action} = $reflection->newInstance(); + } + } + + protected function getViewVariablesConfirmModal($id, $title='', $question='', $actionName='') + { + return [ + 'title' => !empty($title) ? $title : __('Process request {0}', $id), + 'question' => !empty($question) ? $question : __('Confirm request {0}', $id), + 'actionName' => !empty($actionName) ? $actionName : __('Confirm'), + 'path' => ['controller' => 'outbox', 'action' => 'process', $id] + ]; + } + + public function getViewVariables($request) + { + return $this->getViewVariablesConfirmModal($request->id, '', '', ''); + } + + protected function genActionResult($data, $success, $message, $errors=[]) + { + return [ + 'data' => $data, + 'success' => $success, + 'message' => $message, + 'errors' => $errors, + ]; + } + + public function genHTTPReply($controller, $processResult, $redirect=null) + { + $scope = $this->scope; + $action = $this->action; + if ($processResult['success']) { + $message = !empty($processResult['message']) ? $processResult['message'] : __('Request {0} successfully processed.', $id); + if ($controller->ParamHandler->isRest()) { + $response = $controller->RestResponse->viewData($processResult, 'json'); + } else if ($controller->ParamHandler->isAjax()) { + $response = $controller->RestResponse->ajaxSuccessResponse('OutboxProcessor', "{$scope}.{$action}", $processResult['data'], $message); + } else { + $controller->Flash->success($message); + if (!is_null($redirect)) { + $response = $controller->redirect($redirect); + } else { + $response = $controller->redirect(['action' => 'index']); + } + } + } else { + $message = !empty($processResult['message']) ? $processResult['message'] : __('Request {0} could not be processed.', $id); + if ($controller->ParamHandler->isRest()) { + $response = $controller->RestResponse->viewData($processResult, 'json'); + } else if ($controller->ParamHandler->isAjax()) { + $response = $controller->RestResponse->ajaxFailResponse('OutboxProcessor', "{$scope}.{$action}", $processResult['data'], $message, $processResult['errors']); + } else { + $controller->Flash->error($message); + if (!is_null($redirect)) { + $response = $controller->redirect($redirect); + } else { + $response = $controller->redirect(['action' => 'index']); + } + } + } + + return $response; + } + + public function checkLoading() + { + return 'Assimilation successful!'; + } + + public function create($requestData) + { + $user_id = Router::getRequest()->getSession()->read('Auth.id'); + $requestData['scope'] = $this->scope; + $requestData['action'] = $this->action; + $requestData['description'] = $this->description; + $requestData['user_id'] = $user_id; + $request = $this->generateRequest($requestData); + $savedRequest = $this->Outbox->createEntry($request); + return $this->genActionResult( + $savedRequest, + $savedRequest !== false, + __('{0} request for {1} created', $this->scope, $this->action), + $request->getErrors() + ); + } + + public function discard($id, $requestData) + { + $request = $this->Outbox->get($id); + $this->Outbox->delete($request); + return $this->genActionResult( + [], + true, + __('{0}.{1} request #{2} discarded', $this->scope, $this->action, $id) + ); + } +} diff --git a/libraries/default/OutboxProcessors/TemplateOutoxProcessor.php.template b/libraries/default/OutboxProcessors/TemplateOutoxProcessor.php.template new file mode 100644 index 0000000..07dc21f --- /dev/null +++ b/libraries/default/OutboxProcessors/TemplateOutoxProcessor.php.template @@ -0,0 +1,65 @@ +description = __('~to-be-defined~'); + $this->Users = TableRegistry::getTableLocator()->get('Users'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('~to-be-defined~'); + return parent::create($requestData); + } + + public function process($id, $requestData, $inboxRequest) + { + $proposalAccepted = false; + $saveResult = []; + if ($proposalAccepted) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $saveResult, + $proposalAccepted, + $proposalAccepted ? __('success') : __('fail'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php b/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php new file mode 100644 index 0000000..6e0a018 --- /dev/null +++ b/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php @@ -0,0 +1,138 @@ + 'cancel', + 'variant' => 'secondary', + 'text' => __('Cancel'), + ], + [ + 'clickFunction' => 'deleteEntry', + 'variant' => 'danger', + 'text' => __('Delete Message'), + ], + [ + 'clickFunction' => 'resendMessage', + 'text' => __('Re-Send Message'), + ] +]; + +$tools = sprintf( +'
    + %s + %s + %s +
    ', + sprintf('%s%s', + sprintf('/localTools/view/%s', h($request['localTool']->id)), + h($request['localTool']->description), + h($request['localTool']->name), + __('(local tool)') + ), + sprintf('', $this->FontAwesome->getClass('long-arrow-alt-right')), + sprintf('%s%s', + sprintf('/localTools/broodTools/%s', h($request['data']['remote_tool']['id'])), + h($request['data']['remote_tool']['description']), + h($request['data']['remote_tool']['name']), + __('(remote tool)') + ) +); + + +$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' => '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()], +]); + +$requestData = $this->Bootstrap->collapse([ + 'title' => __('Message data'), + 'open' => true, + ], + sprintf('
    %s
    ', json_encode($request['data']['sent'], JSON_PRETTY_PRINT)) +); + +$rows = sprintf('%s%s', __('URL'), h($request['data']['url'])); +$rows .= sprintf('%s%s', __('Reason'), h($request['data']['reason']['message']) ?? ''); +$rows .= sprintf('%s%s', __('Errors'), h(json_encode($request['data']['reason']['errors'])) ?? ''); +$table2 = sprintf('%s
    ', $rows); + +$form = $this->element('genericElements/Form/genericForm', [ + 'entity' => null, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'model' => 'Inbox', + 'fields' => [ + [ + 'field' => 'is_delete', + 'type' => 'checkbox', + 'default' => false + ] + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] +]); +$form = sprintf('
    %s
    ', $form); + +$bodyHtml = sprintf('
    %s
    %s
    %s
    %s
    %s', + $tools, + $table, + $table2, + $requestData, + $form +); + +echo $this->Bootstrap->modal([ + 'title' => $request['title'], + 'size' => 'xl', + 'type' => 'custom', + 'bodyHtml' => sprintf('
    %s
    ', + $bodyHtml + ), + 'footerButtons' => $footerButtons +]); + +?> + + \ No newline at end of file diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index 55c2161..4c77192 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -650,22 +650,55 @@ class ACLComponent extends Component 'url' => '/inbox/index', 'label' => __('Inbox') ], + 'outbox' => [ + 'url' => '/outbox/index', + 'label' => __('Outbox') + ], 'view' => [ 'url' => '/inbox/view/{{id}}', - 'label' => __('View Meta Template'), + 'label' => __('View Message'), 'actions' => ['delete', 'edit', 'view'], 'skipTopMenu' => 1 ], 'delete' => [ 'url' => '/inbox/delete/{{id}}', - 'label' => __('Delete Meta Template'), + 'label' => __('Delete Message'), 'actions' => ['delete', 'edit', 'view'], 'skipTopMenu' => 1, 'popup' => 1 ], 'listProcessors' => [ 'url' => '/inbox/listProcessors', - 'label' => __('List Request Processors'), + 'label' => __('List Inbox Processors'), + 'skipTopMenu' => 1 + ] + ] + ], + 'Outbox' => [ + 'label' => __('Outbox'), + 'url' => '/outbox/index', + 'children' => [ + 'index' => [ + 'url' => '/outbox/index', + 'label' => __('Outbox'), + 'skipTopMenu' => 1 + ], + 'view' => [ + 'url' => '/outbox/view/{{id}}', + 'label' => __('View Message'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/outbox/delete/{{id}}', + 'label' => __('Delete Message'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1, + 'popup' => 1 + ], + 'listProcessors' => [ + 'url' => '/outbox/listProcessors', + 'label' => __('List Outbox Processors'), 'skipTopMenu' => 1 ] ] diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index b71e2d5..0b4f605 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -119,7 +119,7 @@ class InboxController extends AppController $this->set('data', $data); } - public function createInboxEntry($scope, $action) + public function createEntry($scope, $action) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(__('Only POST method is accepted')); @@ -137,7 +137,7 @@ class InboxController extends AppController $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); + return $this->RestResponse->ajaxFailResponse(Inflector::singularize($this->Inbox->getAlias()), 'createEntry', [], $message, $errors); } } else { $processor = $this->inboxProcessors->getProcessor($scope, $action); diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 835b8a1..a1eae55 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -237,7 +237,7 @@ class LocalToolsController extends AppController $response = $this->RestResponse->ajaxSuccessResponse('LocalTool', 'connectionRequest', [], $inboxResult['message']); } else { $this->Flash->success($inboxResult['message']); - $this->redirect(['action' => 'broodTools', $cerebrate_id]); + $response = $this->redirect(['action' => 'broodTools', $cerebrate_id]); } } else { if ($this->ParamHandler->isRest()) { @@ -246,7 +246,7 @@ class LocalToolsController extends AppController $response = $this->RestResponse->ajaxFailResponse('LocalTool', 'connectionRequest', [], $inboxResult['message'], $inboxResult['errors']); } else { $this->Flash->error($inboxResult['message']); - $this->redirect($this->referer()); + $response = $this->redirect($this->referer()); } } return $response; diff --git a/src/Controller/OutboxController.php b/src/Controller/OutboxController.php new file mode 100644 index 0000000..5426883 --- /dev/null +++ b/src/Controller/OutboxController.php @@ -0,0 +1,125 @@ +set('metaGroup', 'Administration'); + } + + + public function index() + { + $this->CRUD->index([ + 'filters' => $this->filters, + 'quickFilters' => ['scope', 'action', ['title' => true], ['comment' => true]], + 'contextFilters' => [ + 'fields' => [ + 'scope', + ] + ], + 'contain' => ['Users'] + ]); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function filtering() + { + $this->CRUD->filtering(); + } + + public function view($id) + { + $this->CRUD->view($id); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function delete($id) + { + $this->set('deletionTitle', __('Discard request')); + $this->set('deletionText', __('Are you sure you want to discard request #{0}?', $id)); + $this->set('deletionConfirm', __('Discard')); + $this->CRUD->delete($id); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function process($id) + { + $request = $this->Outbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); + $scope = $request->scope; + $action = $request->action; + $this->outboxProcessors = TableRegistry::getTableLocator()->get('OutboxProcessors'); + $processor = $this->outboxProcessors->getProcessor($scope, $action); + if ($this->request->is('post')) { + $processResult = $processor->process($id, $this->request->getData(), $request); + return $processor->genHTTPReply($this, $processResult); + } else { + $renderedView = $processor->render($request, $this->request); + return $this->response->withStringBody($renderedView); + } + } + + public function listProcessors() + { + $this->OutboxProcessors = TableRegistry::getTableLocator()->get('OutboxProcessors'); + $processors = $this->OutboxProcessors->listProcessors(); + if ($this->ParamHandler->isRest()) { + return $this->RestResponse->viewData($processors, 'json'); + } + $data = []; + foreach ($processors as $scope => $scopedProcessors) { + foreach ($scopedProcessors as $processor) { + $data[] = [ + 'enabled' => $processor->enabled, + 'scope' => $scope, + 'action' => $processor->action, + 'description' => isset($processor->getDescription) ? $processor->getDescription() : null, + 'notice' => $processor->notice ?? null, + 'error' => $processor->error ?? null, + ]; + } + } + $this->set('data', $data); + } + + public function createEntry($scope, $action) + { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(__('Only POST method is accepted')); + } + $entryData = [ + 'user_id' => $this->ACL->getUser()['id'], + ]; + $entryData['data'] = $this->request->getData() ?? []; + $this->OutboxProcessors = TableRegistry::getTableLocator()->get('OutboxProcessors'); + $processor = $this->OutboxProcessors->getProcessor($scope, $action); + $creationResult = $this->OutboxProcessors->createOutboxEntry($processor, $entryData); + return $processor->genHTTPReply($this, $creationResult); + } +} diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index e1dbd27..570a60b 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -212,11 +212,7 @@ class BroodsTable extends AppTable } else { $response = $http->get($brood->url, $data, $config); } - if ($response->isOk()) { - return $response; - } else { - throw new NotFoundException(__('Could not send to the requested resource.')); - } + return $response; } private function injectRequiredData($params, $data): Array @@ -230,30 +226,30 @@ class BroodsTable extends AppTable public function sendLocalToolConnectionRequest($params, $data): array { - $url = '/inbox/createInboxEntry/LocalTool/IncomingConnectionRequest'; + $url = '/inbox/createEntry/LocalTool/IncomingConnectionRequest'; $data = $this->injectRequiredData($params, $data); - $response = $this->sendRequest($params['remote_cerebrate'], $url, true, $data); try { + $response = $this->sendRequest($params['remote_cerebrate'], $url, true, $data); $jsonReply = $response->getJson(); if (empty($jsonReply['success'])) { - $this->handleMessageNotCreated($response); + $jsonReply = $this->handleMessageNotCreated($params['remote_cerebrate'], $url, $data, 'LocalTool', 'IncomingConnectionRequest', $response, $params); } } catch (NotFoundException $e) { - $jsonReply = $this->handleSendingFailed($response); + $jsonReply = $this->handleSendingFailed($params['remote_cerebrate'], $url, $data, 'LocalTool', 'IncomingConnectionRequest', $e, $params); } return $jsonReply; } public function sendLocalToolAcceptedRequest($params, $data): Response { - $url = '/inbox/createInboxEntry/LocalTool/AcceptedRequest'; + $url = '/inbox/createEntry/LocalTool/AcceptedRequest'; $data = $this->injectRequiredData($params, $data); return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); } public function sendLocalToolDeclinedRequest($params, $data): Response { - $url = '/inbox/createInboxEntry/LocalTool/DeclinedRequest'; + $url = '/inbox/createEntry/LocalTool/DeclinedRequest'; $data = $this->injectRequiredData($params, $data); return $this->sendRequest($params['remote_cerebrate'], $url, true, $data); } @@ -264,10 +260,19 @@ class BroodsTable extends AppTable * @param Object $response * @return array */ - private function handleSendingFailed(Object $response): array + private function handleSendingFailed($brood, $url, $data, $model, $action, $e, $params): array { - // debug('sending failed. Modify state and add entry in outbox'); - throw new NotFoundException(__('sending failed. Modify state and add entry in outbox')); + $reason = [ + 'message' => __('Failed to send message to remote cerebrate. It has been placed in the outbox.'), + 'errors' => [$e->getMessage()], + ]; + $this->saveErrorInOutbox($brood, $url, $data, $reasonMessage, $params); + $creationResult = [ + 'success' => false, + 'message' => $reason['message'], + 'errors' => $reason['errors'], + ]; + return $creationResult; } /** @@ -276,8 +281,43 @@ class BroodsTable extends AppTable * @param Object $response * @return array */ - private function handleMessageNotCreated(Object $response): array + private function handleMessageNotCreated($brood, $url, $data, $model, $action, $response, $params): array { - // debug('Saving message failed. Modify state and add entry in outbox'); + $responseErrors = $response->getStringBody(); + if (!is_null($response->getJson())) { + $responseErrors = $response->getJson()['errors'] ?? $response->getJson()['message']; + } + $reason = [ + 'message' => __('Message rejected by the remote cerebrate. It has been placed in the outbox.'), + 'errors' => [$responseErrors], + ]; + $this->saveErrorInOutbox($brood, $url, $data, $reason, $model, $action, $params); + $creationResult = [ + 'success' => false, + 'message' => $reason['message'], + 'errors' => $reason['errors'], + ]; + return $creationResult; + } + + private function saveErrorInOutbox($brood, $url, $data, $reason, $model, $action, $params): array + { + $this->OutboxProcessors = TableRegistry::getTableLocator()->get('OutboxProcessors'); + $processor = $this->OutboxProcessors->getProcessor('Broods', 'ResendFailedMessage'); + $entryData = [ + 'data' => [ + 'sent' => $data, + 'url' => $url, + 'brood_id' => $brood->id, + 'reason' => $reason, + 'local_tool_id' => $params['connection']['id'], + 'remote_tool' => $params['remote_tool'], + ], + 'brood' => $brood, + 'model' => $model, + 'action' => $action, + ]; + $creationResult = $processor->create($entryData); + return $creationResult; } } diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php index 1640bc2..21d6da6 100644 --- a/src/Model/Table/InboxTable.php +++ b/src/Model/Table/InboxTable.php @@ -86,4 +86,10 @@ class InboxTable extends AppTable } return $errors; } + + public function createEntry($entryData) + { + $savedEntry = $this->save($entryData); + return $savedEntry; + } } diff --git a/src/Model/Table/OutboxProcessorsTable.php b/src/Model/Table/OutboxProcessorsTable.php new file mode 100644 index 0000000..6c6be33 --- /dev/null +++ b/src/Model/Table/OutboxProcessorsTable.php @@ -0,0 +1,134 @@ + [ + 'ResendFailedMessageProcessor' => true, + ], + ]; + + public function initialize(array $config): void + { + parent::initialize($config); + $this->loadProcessors(); + } + + public function getProcessor($scope, $action=null) + { + if (isset($this->outboxProcessors[$scope])) { + if (is_null($action)) { + return $this->outboxProcessors[$scope]; + } else if (!empty($this->outboxProcessors[$scope]->{$action})) { + return $this->outboxProcessors[$scope]->{$action}; + } else { + throw new \Exception(__('Processor {0}.{1} not found', $scope, $action)); + } + } + throw new MissingOutboxProcessorException(__('Processor not found')); + } + + public function listProcessors($scope=null) + { + if (is_null($scope)) { + return $this->outboxProcessors; + } else { + if (isset($this->outboxProcessors[$scope])) { + return $this->outboxProcessors[$scope]; + } else { + throw new MissingOutboxProcessorException(__('Processors for {0} not found', $scope)); + } + } + } + + private function loadProcessors() + { + $processorDir = new Folder($this->processorsDirectory); + $processorFiles = $processorDir->find('.*OutboxProcessor\.php', true); + foreach ($processorFiles as $processorFile) { + if ($processorFile == 'GenericOutboxProcessor.php') { + continue; + } + $processorMainClassName = str_replace('.php', '', $processorFile); + $processorMainClassNameShort = str_replace('OutboxProcessor.php', '', $processorFile); + $processorMainClass = $this->getProcessorClass($processorDir->pwd() . DS . $processorFile, $processorMainClassName); + if (is_object($processorMainClass)) { + $this->outboxProcessors[$processorMainClassNameShort] = $processorMainClass; + foreach ($this->outboxProcessors[$processorMainClassNameShort]->getRegisteredActions() as $registeredAction) { + $scope = $this->outboxProcessors[$processorMainClassNameShort]->getScope(); + if (!empty($this->enabledProcessors[$scope][$registeredAction])) { + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = true; + } else { + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + } + } + } else { + $this->outboxProcessors[$processorMainClassNameShort] = new \stdClass(); + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction} = new \stdClass(); + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction}->action = "N/A"; + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + $this->outboxProcessors[$processorMainClassNameShort]->{$registeredAction}->error = $processorMainClass; + } + } + } + + /** + * getProcessorClass + * + * @param string $filePath + * @param string $processorMainClassName + * @return object|string Object loading success, string containing the error if failure + */ + private function getProcessorClass($filePath, $processorMainClassName) + { + try { + require_once($filePath); + try { + $reflection = new \ReflectionClass($processorMainClassName); + } catch (\ReflectionException $e) { + return $e->getMessage(); + } + $processorMainClass = $reflection->newInstance(true); + if ($processorMainClass->checkLoading() === 'Assimilation successful!') { + return $processorMainClass; + } + } catch (Exception $e) { + return $e->getMessage(); + } + } + + /** + * createOutboxEntry + * + * @param Object|Array $processor can either be the processor object or an array containing data to fetch it + * @param Array $data + * @return Array + */ + public function createOutboxEntry($processor, $data) + { + if (!is_object($processor) && !is_array($processor)) { + throw new MethodNotAllowedException(__("Invalid processor passed")); + } + if (is_array($processor)) { + if (empty($processor['scope']) || empty($processor['action'])) { + throw new MethodNotAllowedException(__("Invalid data passed. Missing either `scope` or `action`")); + } + $processor = $this->getProcessor('User', 'Registration'); + } + return $processor->create($data); + } +} diff --git a/src/Model/Table/OutboxTable.php b/src/Model/Table/OutboxTable.php new file mode 100644 index 0000000..05ed8fc --- /dev/null +++ b/src/Model/Table/OutboxTable.php @@ -0,0 +1,70 @@ +addBehavior('UUID'); + $this->addBehavior('Timestamp', [ + 'events' => [ + 'Model.beforeSave' => [ + 'created' => 'new' + ] + ] + ]); + + $this->belongsTo('Users'); + $this->setDisplayField('title'); + } + + protected function _initializeSchema(TableSchemaInterface $schema): TableSchemaInterface + { + $schema->setColumnType('data', 'json'); + + return $schema; + } + + public function validationDefault(Validator $validator): Validator + { + $validator + ->notEmptyString('scope') + ->notEmptyString('action') + ->notEmptyString('title') + ->datetime('created') + + ->requirePresence([ + 'scope' => ['message' => __('The field `scope` is required')], + 'action' => ['message' => __('The field `action` is required')], + 'title' => ['message' => __('The field `title` is required')], + ], 'create'); + return $validator; + } + + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn('user_id', 'Users'), [ + 'message' => 'The provided `user_id` does not exist' + ]); + + return $rules; + } + + public function createEntry($entryData, $user = null) + { + $savedEntry = $this->save($entryData); + return $savedEntry; + } +} diff --git a/src/View/MonadView.php b/src/View/MonadView.php index f0806a0..18f38d9 100644 --- a/src/View/MonadView.php +++ b/src/View/MonadView.php @@ -23,6 +23,7 @@ class MonadView extends AppView { private $additionalTemplatePaths = [ ROOT . '/libraries/default/InboxProcessors/templates/', + ROOT . '/libraries/default/OutboxProcessors/templates/', ]; protected function _paths(?string $plugin = null, bool $cached = true): array diff --git a/templates/Outbox/index.php b/templates/Outbox/index.php new file mode 100644 index 0000000..79a8d18 --- /dev/null +++ b/templates/Outbox/index.php @@ -0,0 +1,94 @@ +Html->scriptBlock(sprintf( + 'var csrfToken = %s;', + json_encode($this->request->getAttribute('csrfToken')) +)); +echo $this->element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'context_filters', + 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] + ], + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'allowFilering' => true + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'data_path' => 'id', + ], + [ + 'name' => 'created', + 'sort' => 'created', + 'data_path' => 'created', + 'element' => 'datetime' + ], + [ + 'name' => 'scope', + 'sort' => 'scope', + 'data_path' => 'scope', + ], + [ + 'name' => 'action', + 'sort' => 'action', + 'data_path' => 'action', + ], + [ + 'name' => 'title', + 'sort' => 'title', + 'data_path' => 'title', + ], + [ + 'name' => 'user', + 'sort' => 'user_id', + 'data_path' => 'user', + 'element' => 'user' + ], + [ + 'name' => 'description', + 'sort' => 'description', + 'data_path' => 'description', + ], + [ + 'name' => 'comment', + 'sort' => 'comment', + 'data_path' => 'comment', + ], + ], + 'title' => __('Outbox'), + 'description' => __('A list of requests to be manually processed'), + 'actions' => [ + [ + 'url' => '/outbox/view', + 'url_params_data_paths' => ['id'], + 'icon' => 'eye', + 'title' => __('View request') + ], + [ + 'open_modal' => '/outbox/process/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'icon' => 'cogs', + 'title' => __('Process request') + ], + [ + 'open_modal' => '/outbox/delete/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'icon' => 'trash', + 'title' => __('Discard request') + ], + ] + ] +]); +echo '
    '; +?> diff --git a/templates/Outbox/list_processors.php b/templates/Outbox/list_processors.php new file mode 100644 index 0000000..38c87f2 --- /dev/null +++ b/templates/Outbox/list_processors.php @@ -0,0 +1,54 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'skip_pagination' => true, + 'data' => $data, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'context_filters', + 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] + ], + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'allowFilering' => true + ] + ] + ], + 'fields' => [ + [ + 'name' => __('Enabled'), + 'data_path' => 'enabled', + 'element' => 'boolean' + ], + [ + 'name' => __('Processor scope'), + 'data_path' => 'scope', + ], + [ + 'name' => __('Processor action'), + 'data_path' => 'action', + ], + [ + 'name' => __('Description'), + 'data_path' => 'description', + ], + [ + 'name' => __('Notice'), + 'data_path' => 'notice', + ], + [ + 'name' => __('Error'), + 'data_path' => 'error', + ], + ], + 'title' => __('Available Outbox Request Processors'), + 'description' => __('The list of Outbox Request Processors available on this server.'), + 'actions' => [ + ] + ] +]); \ No newline at end of file diff --git a/templates/Outbox/view.php b/templates/Outbox/view.php new file mode 100644 index 0000000..5e6b234 --- /dev/null +++ b/templates/Outbox/view.php @@ -0,0 +1,47 @@ +element( + '/genericElements/SingleViews/single_view', + [ + 'data' => $entity, + 'fields' => [ + [ + 'key' => __('ID'), + 'path' => 'id' + ], + [ + 'key' => 'created', + 'path' => 'created', + ], + [ + 'key' => 'scope', + 'path' => 'scope', + ], + [ + 'key' => 'action', + 'path' => 'action', + ], + [ + 'key' => 'title', + 'path' => 'title', + ], + [ + 'key' => 'user_id', + 'path' => 'user_id', + ], + [ + 'key' => 'description', + 'path' => 'description', + ], + [ + 'key' => 'comment', + 'path' => 'comment', + ], + [ + 'key' => 'data', + 'path' => 'data', + 'type' => 'json' + ], + ], + 'children' => [] + ] +); From 8381bd2c92887ce44b7251055da33d6a3d51bd83 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 21 Jun 2021 08:43:37 +0200 Subject: [PATCH 42/78] chg: [helper:bootstrap] Added card support --- src/View/Helper/BootstrapHelper.php | 100 +++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index 22895fa..f0bfad0 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -86,6 +86,12 @@ class BootstrapHelper extends Helper $bsModal = new BoostrapModal($options); return $bsModal->modal(); } + + public function card($options) + { + $bsCard = new BoostrapCard($options); + return $bsCard->card(); + } public function progress($options) { @@ -609,7 +615,7 @@ class BoostrapTable extends BootstrapGeneric { private function genCaption() { - return $this->genNode('caption', [], h($this->caption)); + return !empty($this->caption) ? $this->genNode('caption', [], h($this->caption)) : ''; } } @@ -917,6 +923,98 @@ class BoostrapModal extends BootstrapGeneric { } } +class BoostrapCard extends BootstrapGeneric +{ + private $defaultOptions = [ + 'variant' => '', + 'headerText' => '', + 'footerText' => '', + 'bodyText' => '', + 'headerHTML' => '', + 'footerHTML' => '', + 'bodyHTML' => '', + 'headerClass' => '', + 'bodyClass' => '', + 'footerClass' => '', + ]; + + public function __construct($options) + { + $this->allowedOptionValues = [ + 'variant' => array_merge(BootstrapGeneric::$variants, ['']), + ]; + $this->processOptions($options); + } + + private function processOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + $this->checkOptionValidity(); + } + + public function card() + { + return $this->genCard(); + } + + private function genCard() + { + $card = $this->genNode('div', [ + 'class' => [ + 'card', + !empty($this->options['variant']) ? "bg-{$this->options['variant']}" : '', + !empty($this->options['variant']) ? $this->getTextClassForVariant($this->options['variant']) : '', + ], + ], implode('', [$this->genHeader(), $this->genBody(), $this->genFooter()])); + return $card; + } + + private function genHeader() + { + if (empty($this->options['headerHTML']) && empty($this->options['headerText'])) { + return ''; + } + $content = !empty($this->options['headerHTML']) ? $this->options['headerHTML'] : h($this->options['headerText']); + $header = $this->genNode('div', [ + 'class' => [ + 'card-header', + h($this->options['headerClass']), + ], + ], $content); + return $header; + } + + private function genBody() + { + if (empty($this->options['bodyHTML']) && empty($this->options['bodyText'])) { + return ''; + } + $content = !empty($this->options['bodyHTML']) ? $this->options['bodyHTML'] : h($this->options['bodyText']); + $body = $this->genNode('div', [ + 'class' => [ + 'card-body', + h($this->options['bodyClass']), + ], + ], $content); + return $body; + } + + private function genFooter() + { + if (empty($this->options['footerHTML']) && empty($this->options['footerText'])) { + return ''; + } + $content = !empty($this->options['footerHTML']) ? $this->options['footerHTML'] : h($this->options['footerText']); + $footer = $this->genNode('div', [ + 'class' => [ + 'card-footer', + h($this->options['footerClass']), + ], + ], $content); + return $footer; + } +} + class BoostrapProgress extends BootstrapGeneric { private $defaultOptions = [ 'value' => 0, From 4856b8abf66f025c10e477b18a4d1f82f7ea62eb Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 21 Jun 2021 08:44:01 +0200 Subject: [PATCH 43/78] chg: [outboxProcessors:Broods] Slight UI improvements for resendFailedMessage --- .../templates/Broods/ResendFailedMessage.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php b/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php index 6e0a018..5775419 100644 --- a/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php +++ b/libraries/default/OutboxProcessors/templates/Broods/ResendFailedMessage.php @@ -80,7 +80,7 @@ $requestData = $this->Bootstrap->collapse([ $rows = sprintf('%s%s', __('URL'), h($request['data']['url'])); $rows .= sprintf('%s%s', __('Reason'), h($request['data']['reason']['message']) ?? ''); $rows .= sprintf('%s%s', __('Errors'), h(json_encode($request['data']['reason']['errors'])) ?? ''); -$table2 = sprintf('%s
    ', $rows); +$table2 = sprintf('%s
    ', $rows); $form = $this->element('genericElements/Form/genericForm', [ 'entity' => null, @@ -102,11 +102,16 @@ $form = $this->element('genericElements/Form/genericForm', [ ]); $form = sprintf('
    %s
    ', $form); -$bodyHtml = sprintf('
    %s
    %s
    %s
    %s
    %s', +$messageSent = $this->Bootstrap->card([ + 'headerHTML' => __('Message Sent'), + 'bodyHTML' => sprintf('%s%s', $table2, $requestData), + 'bodyClass' => 'p-2', +]); + +$bodyHtml = sprintf('
    %s
    %s
    %s
    %s', $tools, $table, - $table2, - $requestData, + $messageSent, $form ); From 19d837a91c76a1a964a8f30b2e353f6f0ba7ac9d Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 21 Jun 2021 08:48:28 +0200 Subject: [PATCH 44/78] fix: [application] Import bodyParser once --- src/Application.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Application.php b/src/Application.php index ee5c853..2da0df1 100644 --- a/src/Application.php +++ b/src/Application.php @@ -28,7 +28,6 @@ use Authentication\AuthenticationService; use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; -use Cake\Http\Middleware\BodyParserMiddleware; use Psr\Http\Message\ServerRequestInterface; /** * Application setup class. From 2b8daf79608b6a025ab958039a028ea14358e9ff Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 21 Jun 2021 14:30:32 +0200 Subject: [PATCH 45/78] chg: [connectorTools] Tracking of connection request state and improved integration with outbox --- .../InboxProcessors/BroodInboxProcessor.php | 2 +- .../InboxProcessors/GenericInboxProcessor.php | 2 +- .../LocalToolInboxProcessor.php | 49 ++++++++-------- .../ProposalInboxProcessor.php | 2 +- .../SynchronisationInboxProcessor.php | 2 +- .../TemplateInboxProcessor.php.template | 2 +- .../InboxProcessors/UserInboxProcessor.php | 2 +- .../templates/LocalTool/GenericRequest.php | 9 ++- .../IncomingConnectionRequest.php | 2 +- .../BroodsOutboxProcessor.php | 35 +++++++++++- .../GenericOutboxProcessor.php | 2 +- .../TemplateOutoxProcessor.php.template | 2 +- .../templates/Broods/ResendFailedMessage.php | 2 +- .../CommonConnectorTools.php | 3 + src/Model/Table/BroodsTable.php | 56 +++++++++++++++---- src/Model/Table/OutboxProcessorsTable.php | 4 +- 16 files changed, 128 insertions(+), 48 deletions(-) diff --git a/libraries/default/InboxProcessors/BroodInboxProcessor.php b/libraries/default/InboxProcessors/BroodInboxProcessor.php index f055c1e..c7d655f 100644 --- a/libraries/default/InboxProcessors/BroodInboxProcessor.php +++ b/libraries/default/InboxProcessors/BroodInboxProcessor.php @@ -22,7 +22,7 @@ class BroodInboxProcessor extends GenericInboxProcessor } } -class ToolInterconnectionProcessor extends BroodInboxProcessor implements GenericProcessorActionI { +class ToolInterconnectionProcessor extends BroodInboxProcessor implements GenericInboxProcessorActionI { public $action = 'ToolInterconnection'; protected $description; diff --git a/libraries/default/InboxProcessors/GenericInboxProcessor.php b/libraries/default/InboxProcessors/GenericInboxProcessor.php index ea71884..1ca84ea 100644 --- a/libraries/default/InboxProcessors/GenericInboxProcessor.php +++ b/libraries/default/InboxProcessors/GenericInboxProcessor.php @@ -5,7 +5,7 @@ use Cake\Utility\Inflector; use Cake\Validation\Validator; use Cake\View\ViewBuilder; -interface GenericProcessorActionI +interface GenericInboxProcessorActionI { public function create($requestData); public function process($requestID, $serverRequest, $inboxRequest); diff --git a/libraries/default/InboxProcessors/LocalToolInboxProcessor.php b/libraries/default/InboxProcessors/LocalToolInboxProcessor.php index 5bddbb0..296ce05 100644 --- a/libraries/default/InboxProcessors/LocalToolInboxProcessor.php +++ b/libraries/default/InboxProcessors/LocalToolInboxProcessor.php @@ -145,11 +145,13 @@ class LocalToolInboxProcessor extends GenericInboxProcessor 'remote_tool' => [ 'id' => $remote_tool_id, 'connector' => $connector->connectorName, + 'name' => $requestData['tool_name'], ], 'remote_org' => $remote_org, 'remote_tool_data' => $requestData, 'remote_cerebrate' => $remoteCerebrate, 'connection' => $connection, + 'connector' => [$connector->connectorName => $connector], ]; } @@ -171,7 +173,7 @@ class LocalToolInboxProcessor extends GenericInboxProcessor } } -class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { +class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor implements GenericInboxProcessorActionI { public $action = 'IncomingConnectionRequest'; protected $description; @@ -212,7 +214,7 @@ class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor impleme $connector = $this->getConnector($inboxRequest); if (!empty($requestData['is_discard'])) { // -> declined $connectorResult = $this->declineConnection($connector, $remoteCerebrate, $inboxRequest['data']); // Fire-and-forget? - $connectionSuccessfull = true; + $connectionSuccessfull = !empty($connectorResult['success']); $resultTitle = __('Could not sent declined message to `{0}`\'s for {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $errors = []; if ($connectionSuccessfull) { @@ -222,16 +224,20 @@ class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor impleme } else { $errors = []; $connectorResult = []; + $thrownErrorMessage = ''; try { $connectorResult = $this->acceptConnection($connector, $remoteCerebrate, $inboxRequest['data']); - $connectionSuccessfull = true; + $connectionSuccessfull = !empty($connectorResult['success']); } catch (\Throwable $th) { $connectionSuccessfull = false; - $errors = $th->getMessage(); + $thrownErrorMessage = $th->getMessage(); } - $resultTitle = __('Could not inter-connect `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $resultTitle = $connectorResult['message'] ?? __('Could not inter-connect `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $errors = $connectorResult['errors'] ?? $thrownErrorMessage; if ($connectionSuccessfull) { $resultTitle = __('Interconnection for `{0}`\'s {1} created', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + } + if ($connectionSuccessfull || !empty($connectorResult['placed_in_outbox'])) { $this->discard($id, $inboxRequest); } } @@ -252,7 +258,7 @@ class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor impleme { $connection = $this->getConnection($requestData); $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); - $connectorResult = $connector->acceptConnection($params); + $connectorResult = $connector->acceptConnectionWrapper($params); $response = $this->sendAcceptedRequestToRemote($params, $connectorResult); return $response; } @@ -261,7 +267,7 @@ class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor impleme { $connection = $this->getConnection($requestData); $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); - $connectorResult = $connector->declineConnection($params); + $connectorResult = $connector->declineConnectionWrapper($params); $response = $this->sendDeclinedRequestToRemote($params, $connectorResult); return $response; } @@ -269,21 +275,17 @@ class IncomingConnectionRequestProcessor extends LocalToolInboxProcessor impleme protected function sendAcceptedRequestToRemote($params, $connectorResult) { $response = $this->Broods->sendLocalToolAcceptedRequest($params, $connectorResult); - // change state if sending fails - // add the entry to the outbox if sending fails. - return $response->getJson(); + return $response; } protected function sendDeclinedRequestToRemote($remoteCerebrate, $connectorResult) { $response = $this->Broods->sendLocalToolDeclinedRequest($params, $connectorResult); - // change state if sending fails - // add the entry to the outbox if sending fails. - return $response->getJson(); + return $response; } } -class AcceptedRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { +class AcceptedRequestProcessor extends LocalToolInboxProcessor implements GenericInboxProcessorActionI { public $action = 'AcceptedRequest'; protected $description; @@ -321,21 +323,22 @@ class AcceptedRequestProcessor extends LocalToolInboxProcessor implements Generi $errors = []; $connectorResult = []; + $thrownErrorMessage = ''; try { - $connectorResult = $this->finalizeConnection($connector, $remoteCerebrate, $inboxRequest['data']); - $connectionSuccessfull = true; + $connectorResult = $this->finaliseConnection($connector, $remoteCerebrate, $inboxRequest['data']); + $connectionSuccessfull = !empty($connectorResult['success']); } catch (\Throwable $th) { $connectionSuccessfull = false; $errors = $th->getMessage(); } - $connectionData = []; $resultTitle = __('Could not finalize inter-connection for `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $errors = $connectorResult['errors'] ?? $thrownErrorMessage; if ($connectionSuccessfull) { $resultTitle = __('Interconnection for `{0}`\'s {1} finalized', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $this->discard($id, $requestData); } return $this->genActionResult( - $connectionData, + $connectorResult, $connectionSuccessfull, $resultTitle, $errors @@ -347,16 +350,18 @@ class AcceptedRequestProcessor extends LocalToolInboxProcessor implements Generi return parent::discard($id, $requestData); } - protected function finalizeConnection($connector, $remoteCerebrate, $requestData) + protected function finaliseConnection($connector, $remoteCerebrate, $requestData) { $connection = $this->getConnection($requestData); $params = $this->genBroodParam($remoteCerebrate, $connection, $connector, $requestData); - $connectorResult = $connector->finaliseConnection($params); - return $connectorResult; + $connectorResult = $connector->finaliseConnectionWrapper($params); + return [ + 'success' => true + ]; } } -class DeclinedRequestProcessor extends LocalToolInboxProcessor implements GenericProcessorActionI { +class DeclinedRequestProcessor extends LocalToolInboxProcessor implements GenericInboxProcessorActionI { public $action = 'DeclinedRequest'; protected $description; diff --git a/libraries/default/InboxProcessors/ProposalInboxProcessor.php b/libraries/default/InboxProcessors/ProposalInboxProcessor.php index baec684..01b6bb7 100644 --- a/libraries/default/InboxProcessors/ProposalInboxProcessor.php +++ b/libraries/default/InboxProcessors/ProposalInboxProcessor.php @@ -22,7 +22,7 @@ class ProposalInboxProcessor extends GenericInboxProcessor } } -class ProposalEditProcessor extends ProposalInboxProcessor implements GenericProcessorActionI { +class ProposalEditProcessor extends ProposalInboxProcessor implements GenericInboxProcessorActionI { public $action = 'ProposalEdit'; protected $description; diff --git a/libraries/default/InboxProcessors/SynchronisationInboxProcessor.php b/libraries/default/InboxProcessors/SynchronisationInboxProcessor.php index b45afc4..2a243cd 100644 --- a/libraries/default/InboxProcessors/SynchronisationInboxProcessor.php +++ b/libraries/default/InboxProcessors/SynchronisationInboxProcessor.php @@ -22,7 +22,7 @@ class SynchronisationInboxProcessor extends GenericInboxProcessor } } -class DataExchangeProcessor extends SynchronisationInboxProcessor implements GenericProcessorActionI { +class DataExchangeProcessor extends SynchronisationInboxProcessor implements GenericInboxProcessorActionI { public $action = 'DataExchange'; protected $description; diff --git a/libraries/default/InboxProcessors/TemplateInboxProcessor.php.template b/libraries/default/InboxProcessors/TemplateInboxProcessor.php.template index 832a588..ef8cf94 100644 --- a/libraries/default/InboxProcessors/TemplateInboxProcessor.php.template +++ b/libraries/default/InboxProcessors/TemplateInboxProcessor.php.template @@ -22,7 +22,7 @@ class SCOPE_InboxProcessor extends GenericInboxProcessor } } -class SCOPE_ACTION_Processor extends ProposalInboxProcessor implements GenericProcessorActionI { +class SCOPE_ACTION_Processor extends ProposalInboxProcessor implements GenericInboxProcessorActionI { public $action = 'ACTION'; protected $description; diff --git a/libraries/default/InboxProcessors/UserInboxProcessor.php b/libraries/default/InboxProcessors/UserInboxProcessor.php index 2c830b1..074ce13 100644 --- a/libraries/default/InboxProcessors/UserInboxProcessor.php +++ b/libraries/default/InboxProcessors/UserInboxProcessor.php @@ -24,7 +24,7 @@ class UserInboxProcessor extends GenericInboxProcessor } } -class RegistrationProcessor extends UserInboxProcessor implements GenericProcessorActionI { +class RegistrationProcessor extends UserInboxProcessor implements GenericInboxProcessorActionI { public $action = 'Registration'; protected $description; diff --git a/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php index 093cd80..845ba5e 100644 --- a/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php @@ -97,7 +97,7 @@ $form = $this->element('genericElements/Form/genericForm', [ ] ] ]); -$localToolHTML = $this->fetch('content', sprintf('
    %s
    ', $form)); +$localToolHTML = $this->fetch('content', sprintf('
    %s
    ', $form));; $requestData = $this->Bootstrap->collapse( [ @@ -129,7 +129,12 @@ echo $this->Bootstrap->modal([ diff --git a/templates/element/genericElements/IndexTable/index_table.php b/templates/element/genericElements/IndexTable/index_table.php index d9b04ec..9a407d4 100644 --- a/templates/element/genericElements/IndexTable/index_table.php +++ b/templates/element/genericElements/IndexTable/index_table.php @@ -93,7 +93,8 @@ } $tbody = '' . $rows . ''; echo sprintf( - '%s%s
    ', + '%s%s
    ', + $tableRandomValue, $tableRandomValue, $this->element( '/genericElements/IndexTable/headers', @@ -114,6 +115,7 @@ ?> +element('genericElements/Form/genericForm', [ + 'entity' => null, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'fields' => [ + [ + 'type' => 'text', + 'field' => 'ids', + 'default' => !empty($id) ? json_encode([$id]) : '' + ] + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] +]); +$formHTML = sprintf('
    %s
    ', $form); + +$bodyMessage = !empty($deletionText) ? __($deletionText) : __('Are you sure you want to delete {0} #{1}?', h(Cake\Utility\Inflector::singularize($this->request->getParam('controller'))), h($id)); +$bodyHTML = sprintf('%s%s', $formHTML, $bodyMessage); + +echo $this->Bootstrap->modal([ + 'size' => 'lg', + 'title' => !empty($deletionTitle) ? $deletionTitle : __('Delete {0}', h(Cake\Utility\Inflector::singularize(Cake\Utility\Inflector::humanize($this->request->getParam('controller'))))), + 'type' => 'confirm-danger', + 'confirmText' => !empty($deletionConfirm) ? $deletionConfirm : __('Delete'), + 'bodyHtml' => $bodyHTML, +]); +?> From 0eb383f627500eb6eb428293cdfd5a9e2b623af3 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:17:39 +0200 Subject: [PATCH 51/78] chg: [bootstrap:helper] Return associated ajaxApi and modalFactory object --- src/View/Helper/BootstrapHelper.php | 2 +- webroot/js/bootstrap-helper.js | 49 +++++++++++++++++++---------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index f0bfad0..b5502d2 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -898,7 +898,7 @@ class BoostrapModal extends BootstrapGeneric { 'text' => h($this->options['confirmText']), 'class' => 'modal-confirm-button', 'params' => [ - 'data-dismiss' => $this->options['confirmFunction'] ? '' : 'modal', + // 'data-dismiss' => $this->options['confirmFunction'] ? '' : 'modal', 'data-confirmFunction' => sprintf('%s', $this->options['confirmFunction']) ] ]))->button(); diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index de52527..a7a3ba1 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -31,7 +31,7 @@ class UIFactory { * @param {ModalFactory~POSTFailCallback} POSTFailCallback - The callback that handles form submissions errors and validation errors. * @return {Promise} Promise object resolving to the ModalFactory object */ - submissionModal(url, POSTSuccessCallback, POSTFailCallback) { + async submissionModal(url, POSTSuccessCallback, POSTFailCallback) { return AJAXApi.quickFetchURL(url).then((modalHTML) => { const theModal = new ModalFactory({ rawHtml: modalHTML, @@ -41,7 +41,7 @@ class UIFactory { theModal.makeModal() theModal.show() theModal.$modal.data('modalObject', theModal) - return theModal + return [theModal, theModal.ajaxApi] }).catch((error) => { UI.toast({ variant: 'danger', @@ -89,6 +89,11 @@ class UIFactory { return UI.submissionReloaderModal(url, reloadUrl, $reloadedElement, $statusNode); } + getContainerForTable($table) { + const tableRandomID = $table.data('table-random-value') + return $table.closest(`#table-container-${tableRandomID}`) + } + /** * Creates and displays a modal where the modal's content is fetched from the provided URL. Reloads the index table after a successful operation. * Supports `displayOnSuccess` option to show another modal after the submission @@ -119,7 +124,7 @@ class UIFactory { $statusNode = $elligibleTable } else { if ($table instanceof jQuery) { - $reloadedElement = $table + $reloadedElement = getContainerForTable($table) $statusNode = $table.find('table.table') } else { $reloadedElement = $(`#table-container-${$table}`) @@ -147,7 +152,6 @@ class UIFactory { */ submissionModalAutoGuess(url, reloadUrl=false, $table=false) { let currentAction = location.pathname.split('/')[2] - currentAction += 'cdsc' if (currentAction !== undefined) { if (currentAction === 'index') { return UI.submissionModalForIndex(url, reloadUrl, $table) @@ -392,6 +396,7 @@ class ModalFactory { if (this.options.backdropStatic) { this.bsModalOptions['backdrop'] = 'static' } + this.ajaxApi = new AJAXApi() } /** @@ -657,8 +662,8 @@ class ModalFactory { const $buttonConfirm = $('') .addClass('btn-' + variant) .text(this.options.confirmText) - .click(this.getConfirmationHandlerFunction()) .attr('data-dismiss', (this.options.closeManually || this.options.closeOnSuccess) ? '' : 'modal') + $buttonConfirm.click(this.getConfirmationHandlerFunction($buttonConfirm)) return [$buttonCancel, $buttonConfirm] } @@ -668,18 +673,28 @@ class ModalFactory { } /** Generate the function that will be called when the user confirm the modal */ - getConfirmationHandlerFunction(i) { + getConfirmationHandlerFunction($buttonConfirm, buttonIndex) { + if (this.options.APIConfirms) { + if (Array.isArray(this.ajaxApi)) { + const tmpApi = new AJAXApi({ + statusNode: $buttonConfirm[0] + }) + this.ajaxApi.push(tmpApi) + } else { + this.ajaxApi.statusNode = $buttonConfirm[0] + this.ajaxApi = [this.ajaxApi]; + } + } else { + this.ajaxApi.statusNode = $buttonConfirm[0] + } return (evt) => { let confirmFunction = this.options.confirm - const tmpApi = new AJAXApi({ - statusNode: evt.target - }) if (this.options.APIConfirms) { - if (i !== undefined && this.options.APIConfirms[i] !== undefined) { - confirmFunction = () => { return this.options.APIConfirms[i](tmpApi) } + if (buttonIndex !== undefined && this.options.APIConfirms[buttonIndex] !== undefined) { + confirmFunction = () => { return this.options.APIConfirms[buttonIndex](this.ajaxApi[buttonIndex]) } } } else if (this.options.APIConfirm) { - confirmFunction = () => { return this.options.APIConfirm(tmpApi) } + confirmFunction = () => { return this.options.APIConfirm(this.ajaxApi) } } let confirmResult = confirmFunction(() => { this.hide() }, this, evt) if (confirmResult === undefined) { @@ -727,7 +742,7 @@ class ModalFactory { } } } - $submitButton.click(selfModal.getConfirmationHandlerFunction(i)) + $submitButton.click(selfModal.getConfirmationHandlerFunction($submitButton, i)) }) } else { let $submitButton = this.$modal.find('.modal-footer #submitButton') @@ -768,19 +783,21 @@ class ModalFactory { return tmpApi.postForm($form[0]) .then((data) => { if (data.success) { - this.options.POSTSuccessCallback(data) + // this.options.POSTSuccessCallback(data) + this.options.POSTSuccessCallback([data, this]) } else { // Validation error this.injectFormValidationFeedback(form, data.errors) return Promise.reject('Validation error'); } }) .catch((errorMessage) => { - this.options.POSTFailCallback(errorMessage) + this.options.POSTFailCallback([errorMessage, this]) + // this.options.POSTFailCallback(errorMessage) return Promise.reject(errorMessage); }) } } - $submitButton.click(this.getConfirmationHandlerFunction()) + $submitButton.click(this.getConfirmationHandlerFunction($submitButton)) } } } From 18e32730d2be3715e0306594f9b3860c3a583125 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:18:51 +0200 Subject: [PATCH 52/78] fix: [genericElement:index_table] Added missing multi-select-actions view --- .../ListTopBar/group_multi_select_actions.php | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 templates/element/genericElements/ListTopBar/group_multi_select_actions.php diff --git a/templates/element/genericElements/ListTopBar/group_multi_select_actions.php b/templates/element/genericElements/ListTopBar/group_multi_select_actions.php new file mode 100644 index 0000000..44616ec --- /dev/null +++ b/templates/element/genericElements/ListTopBar/group_multi_select_actions.php @@ -0,0 +1,74 @@ +Bootstrap->button([ + 'variant' => $child['variant'] ?? 'primary', + 'text' => $child['text'], + 'outline' => !empty($child['outline']), + 'params' => [ + 'data-onclick-function' => $child['onclick'] ?? '', + 'data-table-random-value' => $tableRandomValue, + 'onclick' => 'multiActionClickHandler(this)' + ] + ]); + } + echo sprintf( + '
    %s
    ', + $tableRandomValue, + $buttons + ); + } +?> + + \ No newline at end of file From 2c58de4712465568d46ed5d16ed5a37f5ac79d69 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:19:56 +0200 Subject: [PATCH 53/78] chg: [inbox] Added bulk deletion support --- src/Controller/InboxController.php | 45 ++++++++++++++--- templates/Inbox/index.php | 78 +++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 0b4f605..4b8a788 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -57,17 +57,46 @@ class InboxController extends AppController } } - public function delete($id) + public function delete($id=false) { - if ($this->request->is('post')) { - $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); - $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); - $processor = $this->inboxProcessors->getProcessor($request->scope, $request->action); - $discardResult = $processor->discard($id, $request); - return $processor->genHTTPReply($this, $discardResult); + if ($this->request->is('post')) { // cannot rely on CRUD's delete as inbox's processor discard function is responsible to handle their messages + $ids = $this->CRUD->getIdsOrFail($id); + $discardSuccesses = 0; + $discardResults = []; + $discardErrors = []; + foreach ($ids as $id) { + $request = $this->Inbox->get($id, ['contain' => ['Users' => ['Individuals' => ['Alignments' => 'Organisations']]]]); + $this->inboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); + $processor = $this->inboxProcessors->getProcessor($request->scope, $request->action); + $discardResult = $processor->discard($id, $request); + $discardResults[] = $discardResult; + if ($discardResult['success']) { + $discardSuccesses++; + } else { + $discardErrors[] = $discardResult; + } + } + if (count($ids) == 1) { + return $processor->genHTTPReply($this, $discardResult); + } else { + $success = $discardSuccesses == count($ids); + $message = __('{0} {1} have been discarded.', + $discardSuccesses == count($ids) ? __('All') : sprintf('%s / %s', $discardSuccesses, count($ids)), + sprintf('%s %s', Inflector::singularize($this->Inbox->getAlias()), __('messages')) + ); + $this->CRUD->setResponseForController('delete', $success, $message, $discardResults, $discardResults); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } } $this->set('deletionTitle', __('Discard request')); - $this->set('deletionText', __('Are you sure you want to discard request #{0}?', $id)); + if (!empty($id)) { + $this->set('deletionText', __('Are you sure you want to discard request #{0}?', $id)); + } else { + $this->set('deletionText', __('Are you sure you want to discard the selected requests?')); + } $this->set('deletionConfirm', __('Discard')); $this->CRUD->delete($id); $responsePayload = $this->CRUD->getResponsePayload(); diff --git a/templates/Inbox/index.php b/templates/Inbox/index.php index 55eabd3..c5cc532 100644 --- a/templates/Inbox/index.php +++ b/templates/Inbox/index.php @@ -8,6 +8,16 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'data' => $data, 'top_bar' => [ 'children' => [ + [ + 'children' => [ + [ + 'text' => __('Discard request'), + 'variant' => 'danger', + 'onclick' => 'discardRequests', + ] + ], + 'type' => 'multi_select_actions', + ], [ 'type' => 'context_filters', 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] @@ -23,6 +33,15 @@ echo $this->element('genericElements/IndexTable/index_table', [ ] ], 'fields' => [ + [ + 'element' => 'selector', + 'class' => 'short', + 'data' => [ + 'id' => [ + 'value_path' => 'id' + ] + ] + ], [ 'name' => '#', 'sort' => 'id', @@ -95,5 +114,62 @@ echo $this->element('genericElements/IndexTable/index_table', [ ] ] ]); -echo ''; ?> + + \ No newline at end of file From 01467b94ea31258b895a527e8ea7ce22e0a601ba Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:33:36 +0200 Subject: [PATCH 54/78] fix: [helpers:bootstrap] Correct call to local function --- webroot/js/bootstrap-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index a7a3ba1..2d50a49 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -124,7 +124,7 @@ class UIFactory { $statusNode = $elligibleTable } else { if ($table instanceof jQuery) { - $reloadedElement = getContainerForTable($table) + $reloadedElement = this.getContainerForTable($table) $statusNode = $table.find('table.table') } else { $reloadedElement = $(`#table-container-${$table}`) From 4899ede9cc93651b6433abcc383fac780b710515 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:34:06 +0200 Subject: [PATCH 55/78] fix: [inbox] Typo --- templates/Inbox/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Inbox/index.php b/templates/Inbox/index.php index c5cc532..b9d02e1 100644 --- a/templates/Inbox/index.php +++ b/templates/Inbox/index.php @@ -11,7 +11,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ [ 'children' => [ [ - 'text' => __('Discard request'), + 'text' => __('Discard requests'), 'variant' => 'danger', 'onclick' => 'discardRequests', ] From e933d6ca3b27bc1d0a40a8aa78e5da9d65143c08 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 23 Jun 2021 11:34:26 +0200 Subject: [PATCH 56/78] chg: [outbox] Support of bulk deletion --- src/Controller/OutboxController.php | 12 ++++-- templates/Outbox/index.php | 59 ++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/Controller/OutboxController.php b/src/Controller/OutboxController.php index 5426883..946a9ea 100644 --- a/src/Controller/OutboxController.php +++ b/src/Controller/OutboxController.php @@ -57,11 +57,15 @@ class OutboxController extends AppController } } - public function delete($id) + public function delete($id=false) { - $this->set('deletionTitle', __('Discard request')); - $this->set('deletionText', __('Are you sure you want to discard request #{0}?', $id)); - $this->set('deletionConfirm', __('Discard')); + $this->set('deletionTitle', __('Confirm message deletion')); + if (!empty($id)) { + $this->set('deletionText', __('Are you sure you want to delete message #{0}?', $id)); + } else { + $this->set('deletionText', __('Are you sure you want to delete the selected messages?')); + } + $this->set('deletionConfirm', __('Delete')); $this->CRUD->delete($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { diff --git a/templates/Outbox/index.php b/templates/Outbox/index.php index 79a8d18..c7ac4f9 100644 --- a/templates/Outbox/index.php +++ b/templates/Outbox/index.php @@ -8,6 +8,16 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'data' => $data, 'top_bar' => [ 'children' => [ + [ + 'children' => [ + [ + 'text' => __('Delete messages'), + 'variant' => 'danger', + 'onclick' => 'deleteMessages', + ] + ], + 'type' => 'multi_select_actions', + ], [ 'type' => 'context_filters', 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] @@ -23,6 +33,15 @@ echo $this->element('genericElements/IndexTable/index_table', [ ] ], 'fields' => [ + [ + 'element' => 'selector', + 'class' => 'short', + 'data' => [ + 'id' => [ + 'value_path' => 'id' + ] + ] + ], [ 'name' => '#', 'sort' => 'id', @@ -90,5 +109,43 @@ echo $this->element('genericElements/IndexTable/index_table', [ ] ] ]); -echo ''; ?> + + From 1d801749876fe986ac4ff9598c05222f43e4a8eb Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:45:12 +0200 Subject: [PATCH 57/78] fix: [outboxProcessor:broods] Do not force connector name anymore --- libraries/default/OutboxProcessors/BroodsOutboxProcessor.php | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php index 34a06e8..946e548 100644 --- a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php +++ b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php @@ -108,7 +108,6 @@ class ResendFailedMessageProcessor extends BroodsOutboxProcessor implements Gene } else { $url = $outboxRequest->data['url']; $dataSent = $outboxRequest->data['sent']; - $dataSent['connectorName'] = 'MispConnector'; $response = $this->Broods->sendRequest($brood, $url, true, $dataSent); $jsonReply = $response->getJson(); $success = !empty($jsonReply['success']); From bda5eef007993ccc1604b37d17694e250507760c Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:46:07 +0200 Subject: [PATCH 58/78] fix: [localTool:commonConnector] Call localtool's finalise connection function --- src/Lib/default/local_tool_connectors/CommonConnectorTools.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php index 9fee5c5..7e865c0 100644 --- a/src/Lib/default/local_tool_connectors/CommonConnectorTools.php +++ b/src/Lib/default/local_tool_connectors/CommonConnectorTools.php @@ -104,6 +104,7 @@ class CommonConnectorTools public function finaliseConnectionWrapper(array $params): bool { + $result = $this->finaliseConnection($params); $this->remoteToolConnectionStatus($params, self::STATE_CONNECTED); return false; } From 3b0c2d2dcaeb66af78152f0b68e563c327166706 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:47:20 +0200 Subject: [PATCH 59/78] chg: [localTolls:broods] Reload correct index instead of redirect --- templates/LocalTools/brood_tools.php | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/LocalTools/brood_tools.php b/templates/LocalTools/brood_tools.php index d069eca..87a8508 100644 --- a/templates/LocalTools/brood_tools.php +++ b/templates/LocalTools/brood_tools.php @@ -43,6 +43,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'actions' => [ [ 'open_modal' => sprintf('/localTools/connectionRequest/%s/[onclick_params_data_path]', h($id)), + 'reload_url' => $this->Url->build(['action' => 'broodTools', $id]), 'modal_params_data_path' => 'id', 'title' => 'Issue a connection request', 'icon' => 'plug' From 014bfaf70ab82f20a867ad5baf769847c962c2d2 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:50:33 +0200 Subject: [PATCH 60/78] chg: [localTools:MISP] Include user_id and reflected it - So that the initiator cerebrate knows which user has to be enabled --- src/Lib/default/local_tool_connectors/MispConnector.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 1d3457c..19af9dd 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -699,8 +699,9 @@ class MispConnector extends CommonConnectorTools $params['sync_user'] = $this->createSyncUser($params); return [ 'email' => $params['sync_user']['email'], + 'user_id' => $params['sync_user']['id'], 'authkey' => $params['sync_user']['authkey'], - 'url' => $params['connection_settings']['url'] + 'url' => $params['connection_settings']['url'], ]; } @@ -721,7 +722,8 @@ class MispConnector extends CommonConnectorTools return [ 'email' => $params['sync_user']['email'], 'authkey' => $params['sync_user']['authkey'], - 'url' => $params['connection_settings']['url'] + 'url' => $params['connection_settings']['url'], + 'reflected_user_id' => $params['remote_tool_data']['user_id'] // request initiator Cerebrate to enable the MISP user ]; } From 6b312ba65b146788555005ab2de1094d37b6beed Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:52:39 +0200 Subject: [PATCH 61/78] fix: [localTool:MISP] Effectively enable user on connection request finalisation --- .../local_tool_connectors/MispConnector.php | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 19af9dd..c087691 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -696,7 +696,7 @@ class MispConnector extends CommonConnectorTools { $params['connection_settings'] = json_decode($params['connection']['settings'], true); $params['misp_organisation'] = $this->getSetOrg($params); - $params['sync_user'] = $this->createSyncUser($params); + $params['sync_user'] = $this->createSyncUser($params, true); return [ 'email' => $params['sync_user']['email'], 'user_id' => $params['sync_user']['id'], @@ -710,7 +710,7 @@ class MispConnector extends CommonConnectorTools $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_user'] = $this->createSyncUser($params, false); $serverParams = $params; $serverParams['body'] = [ 'authkey' => $params['remote_tool_data']['authkey'], @@ -730,6 +730,7 @@ class MispConnector extends CommonConnectorTools public function finaliseConnection(array $params): bool { $params['misp_organisation'] = $this->getSetOrg($params); + $user = $this->enableUser($params, intval($params['remote_tool_data']['reflected_user_id'])); $serverParams = $params; $serverParams['body'] = [ 'authkey' => $params['remote_tool_data']['authkey'], @@ -771,20 +772,29 @@ class MispConnector extends CommonConnectorTools return $organisation; } - private function createSyncUser(array $params): array + private function createSyncUser(array $params, $disabled=true): 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, + 'disabled' => $disabled, 'change_pw' => 0, 'termsaccepted' => 1 ]; return $this->createUser($user, $params); } + private function enableUser(array $params, int $userID): array + { + $params['softError'] = 1; + $user = [ + 'disabled' => false, + ]; + return $this->updateUser($userID, $user, $params); + } + private function addServer(array $params): array { if ( @@ -817,6 +827,16 @@ class MispConnector extends CommonConnectorTools } return $response->getJson()['User']; } + + private function updateUser(int $userID, array $user, array $params): array + { + $params['body'] = $user; + $response = $this->postData(sprintf('/admin/users/edit/%s', $userID), $params); + if (!$response->isOk()) { + throw new MethodNotAllowedException(__('Could not edit the user in MISP.')); + } + return $response->getJson()['User']; + } } ?> From ab6d4eb8c6354d03292c5a808e98c3d8b58f2064 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:53:16 +0200 Subject: [PATCH 62/78] chg: [localTool:MISP] Set a meaningfull default name for the server if none is provided --- src/Lib/default/local_tool_connectors/MispConnector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index c087691..6843dd2 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -715,7 +715,7 @@ class MispConnector extends CommonConnectorTools $serverParams['body'] = [ 'authkey' => $params['remote_tool_data']['authkey'], 'url' => $params['remote_tool_data']['url'], - 'name' => !empty($params['remote_tool_data']['name']) ? $params['remote_tool_data']['name'] : 'Empty name fix me', + 'name' => !empty($params['remote_tool_data']['tool_name']) ? $params['remote_tool_data']['tool_name'] : sprintf('MISP for %s', $params['remote_tool_data']['url']), 'remote_org_id' => $params['misp_organisation']['id'] ]; $params['sync_connection'] = $this->addServer($serverParams); @@ -735,7 +735,7 @@ class MispConnector extends CommonConnectorTools $serverParams['body'] = [ 'authkey' => $params['remote_tool_data']['authkey'], 'url' => $params['remote_tool_data']['url'], - 'name' => !empty($params['remote_tool_data']['name']) ? $params['remote_tool_data']['name'] : 'Empty name fix me', + 'name' => !empty($params['remote_tool_data']['tool_name']) ? $params['remote_tool_data']['tool_name'] : sprintf('MISP for %s', $params['remote_tool_data']['url']), 'remote_org_id' => $params['misp_organisation']['id'] ]; $params['sync_connection'] = $this->addServer($serverParams); From 1be4978f28c4191b94128610901c8819cb3dcafb Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 08:54:08 +0200 Subject: [PATCH 63/78] chg: [localTool:MISP] Indentation --- src/Lib/default/local_tool_connectors/MispConnector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 6843dd2..0fbc772 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -202,8 +202,8 @@ class MispConnector extends CommonConnectorTools $url = $this->urlAppendParams($url, $params); $response = $http->post($settings['url'] . $url, json_encode($params['body']), [ 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'application/json', + 'AUTHORIZATION' => $settings['authkey'], + 'Accept' => 'application/json', ], 'type' => 'json', ]); From 63928e756a707726c4ed362a903750aec6bc9417 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 09:36:47 +0200 Subject: [PATCH 64/78] chg: [brood] Centralized methods to issue requests --- src/Model/Table/BroodsTable.php | 95 +++++++++++++-------------------- 1 file changed, 37 insertions(+), 58 deletions(-) diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index b6e1a62..0e3a2b0 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -29,18 +29,40 @@ class BroodsTable extends AppTable return $validator; } + public function genHTTPClient(Object $brood, array $options=[]): Object + { + $defaultOptions = [ + 'headers' => [ + 'Authorization' => $brood->authkey, + ], + ]; + if (empty($options['type'])) { + $options['type'] = 'json'; + } + $options = array_merge($defaultOptions, $options); + $http = new Client($options); + return $http; + } + + public function HTTPClientGET(String $relativeURL, Object $brood, array $data=[], array $options=[]): Object + { + $http = $this->genHTTPClient($brood, $options); + $url = sprintf('%s%s', $brood->url, $relativeURL); + return $http->get($url, $data, $options); + } + + public function HTTPClientPOST(String $relativeURL, Object $brood, $data, array $options=[]): Object + { + $http = $this->genHTTPClient($brood, $options); + $url = sprintf('%s%s', $brood->url, $relativeURL); + return $http->post($url, $data, $options); + } + public function queryStatus($id) { $brood = $this->find()->where(['id' => $id])->first(); - $http = new Client(); $start = microtime(true); - $response = $http->get($brood['url'] . '/instance/status.json', [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $this->HTTPClientGET('/instance/status.json', $brood); $ping = ((int)(100 * (microtime(true) - $start))); $errors = [ 403 => [ @@ -84,15 +106,8 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $http = new Client(); $filterQuery = empty($filter) ? '' : '?quickFilter=' . urlencode($filter); - $response = $http->get($brood['url'] . '/' . $scope . '/index.json' . $filterQuery , [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $this->HTTPClientGET(sprintf('/%s/index.json%s', $scope, $filterQuery), $brood); if ($response->isOk()) { return $response->getJson(); } else { @@ -107,14 +122,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $http = new Client(); - $response = $http->get($brood['url'] . '/' . $scope . '/view/' . $org_id . '/index.json' , [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $this->HTTPClientGET(sprintf('/%s/view/%s/index.json', $scope, $org_id), $brood); if ($response->isOk()) { $org = $response->getJson(); $this->Organisation = TableRegistry::getTableLocator()->get('Organisations'); @@ -132,14 +140,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $http = new Client(); - $response = $http->get($brood['url'] . '/organisations/view/' . $org_id . '/index.json' , [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $this->HTTPClientGET(sprintf('/organisations/view/%s/index.json', $org_id), $brood); if ($response->isOk()) { $org = $response->getJson(); $this->Organisation = TableRegistry::getTableLocator()->get('Organisations'); @@ -157,14 +158,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $http = new Client(); - $response = $http->get($brood['url'] . '/individuals/view/' . $individual_id . '/index.json' , [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'], - 'Accept' => 'Application/json', - 'Content-type' => 'Application/json' - ] - ]); + $response = $this->HTTPClientGET(sprintf('/individuals/view/%s/index.json', $individual_id), $brood); if ($response->isOk()) { $org = $response->getJson(); $this->Individual = TableRegistry::getTableLocator()->get('Individual'); @@ -182,13 +176,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $http = new Client(); - $response = $http->get($brood['url'] . '/localTools/exposedTools' , [], [ - 'headers' => [ - 'Authorization' => $brood['authkey'] - ], - 'type' => 'json' - ]); + $response = $this->HTTPClientGET('/localTools/exposedTools', $brood); if ($response->isOk()) { return $response->getJson(); } else { @@ -198,19 +186,10 @@ class BroodsTable extends AppTable public function sendRequest($brood, $urlPath, $methodPost = true, $data = []): Response { - $http = new Client(); - $config = [ - 'headers' => [ - 'AUTHORIZATION' => $brood->authkey, - 'Accept' => 'application/json' - ], - 'type' => 'json' - ]; - $url = $brood->url . $urlPath; if ($methodPost) { - $response = $http->post($url, json_encode($data), $config); + $response = $this->HTTPClientPOST($urlPath, $brood, json_encode($data)); } else { - $response = $http->get($brood->url, $data, $config); + $response = $this->HTTPClientGET($urlPath, $brood, $data); } return $response; } From b90fbf39747ff2a47653c11e173235d1970bb56d Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 10:18:49 +0200 Subject: [PATCH 65/78] chg: [localtTools:MISP] Centralized methods to issue requests --- .../local_tool_connectors/MispConnector.php | 65 ++++++++----------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 0fbc772..47974d7 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -101,41 +101,48 @@ class MispConnector extends CommonConnectorTools } } - private function getHeaders(array $connectionSettings): array - { - return [ - 'AUTHORIZATION' => $connectionSettings['authkey'], - 'Accept' => 'application/json', - 'Content-type' => 'application/json' - ]; - } - - private function getHTTPClient(Object $connection): Object + private function genHTTPClient(Object $connection, array $options=[]): Object { $settings = json_decode($connection->settings, true); - $options = []; + $defaultOptions = [ + 'headers' => [ + 'Authorization' => $settings['authkey'], + ], + ]; + if (empty($options['type'])) { + $options['type'] = 'json'; + } if (!empty($settings['skip_ssl'])) { $options['ssl_verify_peer'] = false; $options['ssl_verify_host'] = false; $options['ssl_verify_peer_name'] = false; $options['ssl_allow_self_signed'] = true; } + $options = array_merge($defaultOptions, $options); $http = new Client($options); return $http; } - public function health(Object $connection): array + public function HTTPClientGET(String $relativeURL, Object $connection, array $data=[], array $options=[]): Object { $settings = json_decode($connection->settings, true); - $http = $this->getHTTPClient($connection); + $http = $this->genHTTPClient($connection, $options); + $url = sprintf('%s%s', $settings['url'], $relativeURL); + return $http->get($url, $data, $options); + } + public function HTTPClientPOST(String $relativeURL, Object $connection, $data, array $options=[]): Object + { + $settings = json_decode($connection->settings, true); + $http = $this->genHTTPClient($connection, $options); + $url = sprintf('%s%s', $settings['url'], $relativeURL); + return $http->post($url, $data, $options); + } + + public function health(Object $connection): array + { try { - $response = $http->post($settings['url'] . '/users/view/me.json', '{}', ['headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'application/json', - ], - 'type' => 'json', - ]); + $response = $this->HTTPClientPOST('/users/view/me.json', $connection, '{}'); } catch (\Exception $e) { return [ 'status' => 0, @@ -165,8 +172,6 @@ class MispConnector extends CommonConnectorTools if (empty($params['connection'])) { throw new NotFoundException(__('No connection object received.')); } - $settings = json_decode($params['connection']->settings, true); - $http = $this->getHTTPClient($params['connection']); if (!empty($params['sort'])) { $list = explode('.', $params['sort']); $params['sort'] = end($list); @@ -175,13 +180,7 @@ class MispConnector extends CommonConnectorTools $params['limit'] = 50; } $url = $this->urlAppendParams($url, $params); - $response = $http->get($settings['url'] . $url, false, [ - 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'application/json', - 'Content-type' => 'application/json' - ] - ]); + $response = $this->HTTPClientGET($url, $params['connection']); if ($response->isOk()) { return $response; } else { @@ -197,16 +196,8 @@ class MispConnector extends CommonConnectorTools if (empty($params['connection'])) { throw new NotFoundException(__('No connection object received.')); } - $settings = json_decode($params['connection']->settings, true); - $http = $this->getHTTPClient($params['connection']); $url = $this->urlAppendParams($url, $params); - $response = $http->post($settings['url'] . $url, json_encode($params['body']), [ - 'headers' => [ - 'AUTHORIZATION' => $settings['authkey'], - 'Accept' => 'application/json', - ], - 'type' => 'json', - ]); + $response = $this->HTTPClientPOST($url, $params['connection'], json_encode($params['body'])); if ($response->isOk()) { return $response; } else { From 7ed72c54695c624533885d27fee2a8e64e615087 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 28 Jun 2021 10:45:23 +0200 Subject: [PATCH 66/78] chg: [wip] local tool interconnector, user browsing/searching added for misp connector --- src/Controller/LocalToolsController.php | 30 ++++++ .../local_tool_connectors/MispConnector.php | 92 ++++++++++++++++++- src/Model/Table/LocalToolsTable.php | 44 +++++++++ templates/LocalTools/connector_index.php | 5 + 4 files changed, 168 insertions(+), 3 deletions(-) diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 61f6e92..da34849 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -246,4 +246,34 @@ class LocalToolsController extends AppController ]); } } + + public function connectLocal($local_tool_id) + { + $params = [ + 'local_tool_id' => $local_tool_id + ]; + $local_tool = $this->LocalTools->fetchConnection($local_tool_id); + if ($this->request->is(['post', 'put'])) { + $postParams = $this->ParamHandler->harvestParams(['target_tool_id']); + if (empty($postParams['target_tool_id'])) { + throw new MethodNotAllowedException(__('No target tool ID supplied.')); + } + + $params['target_tool_id'] = $postParams['target_tool_id']; + $result = $this->LocalTools->encodeLocalConnection($params); + // Send message to remote inbox + debug($result); + } else { + $target_tools = $this->LocalTools->findConnectable($local_tool); + debug($target_tools); + if (empty($target_tools)) { + throw new NotFoundException(__('No tools found to connect.')); + } + $this->set('data', [ + 'remoteCerebrate' => $remoteCerebrate, + 'remoteTool' => $remoteTool, + 'local_tools' => $local_tools + ]); + } + } } diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index efd480e..d221f28 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -82,6 +82,17 @@ class MispConnector extends CommonConnectorTools 'sort', 'direction' ] + ], + 'usersAction' => [ + 'type' => 'index', + 'scope' => 'child', + 'params' => [ + 'quickFilter', + 'limit', + 'page', + 'sort', + 'direction' + ] ] ]; public $version = '0.1'; @@ -397,6 +408,82 @@ class MispConnector extends CommonConnectorTools } } + public function usersAction(array $params): array + { + $params['validParams'] = [ + 'limit' => 'limit', + 'page' => 'page', + 'quickFilter' => 'searchall' + ]; + $urlParams = h($params['connection']['id']) . '/usersAction'; + $response = $this->getData('/admin/users/index', $params); + $data = $response->getJson(); + if (!empty($data)) { + return [ + 'type' => 'index', + 'data' => [ + 'data' => $data, + 'skip_pagination' => 1, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'additionalUrlParams' => $urlParams, + 'quickFilter' => 'value' + ] + ] + ], + 'fields' => [ + [ + 'name' => 'Id', + 'sort' => 'User.id', + 'data_path' => 'User.id', + ], + [ + 'name' => 'Organisation', + 'sort' => 'Organisation.name', + 'data_path' => 'Organisation.name', + ], + [ + 'name' => 'Email', + 'sort' => 'User.email', + 'data_path' => 'User.email', + ], + [ + 'name' => 'Role', + 'sort' => 'Role.name', + 'data_path' => 'Role.name' + ] + ], + 'actions' => [ + [ + 'open_modal' => '/localTools/action/' . h($params['connection']['id']) . '/editUser?id={{0}}', + 'modal_params_data_path' => ['User.id'], + 'icon' => 'edit', + 'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/editAction' + ], + [ + 'open_modal' => '/localTools/action/' . h($params['connection']['id']) . '/deleteUser?id={{0}}', + 'modal_params_data_path' => ['User.id'], + 'icon' => 'trash', + 'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/serversAction' + ] + ], + 'title' => false, + 'description' => false, + 'pull' => 'right' + ] + ]; + } else { + return []; + } + } + + public function organisationsAction(array $params): array { $params['validParams'] = [ @@ -631,8 +718,7 @@ class MispConnector extends CommonConnectorTools 'label' => __('Value'), 'default' => h($response['value']), 'type' => 'dropdown', - 'options' => $response['options'], - + 'options' => $response['options'] ] ]; } else { @@ -648,7 +734,7 @@ class MispConnector extends CommonConnectorTools return [ 'data' => [ 'title' => __('Modify server setting'), - 'description' => __('Modify setting ({0}) on connected MISP instance.', $params['setting']), + 'description' => __('Modify setting ({0}) on selected MISP instance(s).', $params['setting']), 'fields' => $fields, 'submit' => [ 'action' => $params['request']->getParam('action') diff --git a/src/Model/Table/LocalToolsTable.php b/src/Model/Table/LocalToolsTable.php index 9cfc48f..5caa12b 100644 --- a/src/Model/Table/LocalToolsTable.php +++ b/src/Model/Table/LocalToolsTable.php @@ -95,6 +95,29 @@ class LocalToolsTable extends AppTable return $connectors; } + public function getInterconnectors(string $name = null): array + { + $connectors = []; + $dirs = [ + ROOT . '/src/Lib/default/local_tool_interconnectors', + ROOT . '/src/Lib/custom/local_tool_interconnectors' + ]; + foreach ($dirs as $dir) { + $dir = new Folder($dir); + $files = $dir->find('.*Interconnector\.php'); + foreach ($files as $file) { + require_once($dir->pwd() . '/'. $file); + $className = substr($file, 0, -4); + $classNamespace = '\\' . $className . '\\' . $className; + $tempClass = new $classNamespace; + if (empty($name) || $tempClass->getConnectors()[0] === $name) { + $connectors[$tempClass->getConnectors()[0]][] = new $classNamespace; + } + } + } + return $connectors; + } + public function extractMeta(array $connector_classes, bool $includeConnections = false): array { $connectors = []; @@ -236,4 +259,25 @@ class LocalToolsTable extends AppTable } return $local_tools; } + + public function findConnectable($local_tool): array + { + $connectors = $this->getInterconnectors($local_tool['connector']); + $validTargets = []; + if (!empty($connectors)) { + foreach ($connectors[$local_tool['connector']] as $connector) { + $validTargets[$connector['connects'][1]] = 1; + } + } + + } + + public function fetchConnection($id): object + { + $connection = $this->find()->where(['id' => $id])->first(); + if (empty($connection)) { + throw new NotFoundException(__('Local tool not found.')); + } + return $connection; + } } diff --git a/templates/LocalTools/connector_index.php b/templates/LocalTools/connector_index.php index 8a2bf4e..5b2a57c 100644 --- a/templates/LocalTools/connector_index.php +++ b/templates/LocalTools/connector_index.php @@ -60,6 +60,11 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'url_params_data_paths' => ['id'], 'icon' => 'eye' ], + [ + 'open_modal' => '/localTools/connectLocal/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'icon' => 'plug' + ], [ 'open_modal' => '/localTools/edit/[onclick_params_data_path]', 'modal_params_data_path' => 'id', From 6e2a541e4fceff0721b4d1ef1dca4ab079777244 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 28 Jun 2021 10:46:19 +0200 Subject: [PATCH 67/78] chg: [wip] local tool interconnector library created --- .../CommonTools.php | 24 +++++++++++++++++++ .../MispToMispInterconnector.php | 20 ++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/Lib/default/local_tool_interconnectors/CommonTools.php create mode 100644 src/Lib/default/local_tool_interconnectors/MispToMispInterconnector.php diff --git a/src/Lib/default/local_tool_interconnectors/CommonTools.php b/src/Lib/default/local_tool_interconnectors/CommonTools.php new file mode 100644 index 0000000..cb50bc0 --- /dev/null +++ b/src/Lib/default/local_tool_interconnectors/CommonTools.php @@ -0,0 +1,24 @@ +connects; + } +} + +?> diff --git a/src/Lib/default/local_tool_interconnectors/MispToMispInterconnector.php b/src/Lib/default/local_tool_interconnectors/MispToMispInterconnector.php new file mode 100644 index 0000000..ef61de6 --- /dev/null +++ b/src/Lib/default/local_tool_interconnectors/MispToMispInterconnector.php @@ -0,0 +1,20 @@ + From 95299430b83ad719afbecf58e5f83db3bc842e68 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 10:50:29 +0200 Subject: [PATCH 68/78] chg: [migrations:roles] Added perm_sync column --- .../20210628104235_RolesPermSync.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 config/Migrations/20210628104235_RolesPermSync.php diff --git a/config/Migrations/20210628104235_RolesPermSync.php b/config/Migrations/20210628104235_RolesPermSync.php new file mode 100644 index 0000000..04c36ae --- /dev/null +++ b/config/Migrations/20210628104235_RolesPermSync.php @@ -0,0 +1,18 @@ +table('roles') + ->addColumn('perm_sync', 'boolean', [ + 'default' => 0, + 'null' => false, + ]) + ->update(); + } +} \ No newline at end of file From 32c94e544da8ee680910cd7fed58b223e76bb195 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 11:30:23 +0200 Subject: [PATCH 69/78] chg: [inboxProcessors] Typo --- libraries/default/InboxProcessors/LocalToolInboxProcessor.php | 2 +- .../InboxProcessors/templates/LocalTool/GenericRequest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/default/InboxProcessors/LocalToolInboxProcessor.php b/libraries/default/InboxProcessors/LocalToolInboxProcessor.php index f4839cb..8852f9e 100644 --- a/libraries/default/InboxProcessors/LocalToolInboxProcessor.php +++ b/libraries/default/InboxProcessors/LocalToolInboxProcessor.php @@ -331,7 +331,7 @@ class AcceptedRequestProcessor extends LocalToolInboxProcessor implements Generi $connectionSuccessfull = false; $errors = $th->getMessage(); } - $resultTitle = __('Could not finalize inter-connection for `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); + $resultTitle = __('Could not finalise inter-connection for `{0}`\'s {1}', $inboxRequest['origin'], $inboxRequest['local_tool_name']); $errors = $connectorResult['errors'] ?? $thrownErrorMessage; if ($connectionSuccessfull) { $resultTitle = __('Interconnection for `{0}`\'s {1} finalised', $inboxRequest['origin'], $inboxRequest['local_tool_name']); diff --git a/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php b/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php index 9146180..15ea91b 100644 --- a/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php +++ b/libraries/default/InboxProcessors/templates/LocalTool/GenericRequest.php @@ -11,7 +11,7 @@ $defaultSteps = [ 'text' => __('Request Accepted'), 'icon' => 'check-square', 'title' => __(''), - 'confirmButton' => __('Finalize Connection') + 'confirmButton' => __('Finalise Connection') ], [ 'text' => __('Connection Done'), From 39f44471e4f49ae097df1046520c243359a1104c Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 11:35:31 +0200 Subject: [PATCH 70/78] chg: [ACLComponent] Moved DB migration to administration tab --- src/Controller/Component/ACLComponent.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index 4c77192..8367536 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -760,7 +760,17 @@ class ACLComponent extends Component 'skipTopMenu' => 1 ] ] - ] + ], + 'Instance' => [ + __('Instance'), + 'url' => '/instance/home', + 'children' => [ + 'migration' => [ + 'url' => '/instance/migrationIndex', + 'label' => __('Database migration') + ] + ] + ], ], 'Cerebrate' => [ 'Roles' => [ @@ -794,10 +804,6 @@ class ACLComponent extends Component 'url' => '/instance/home', 'label' => __('Home') ], - 'migration' => [ - 'url' => '/instance/migrationIndex', - 'label' => __('Database migration') - ] ] ], 'Users' => [ From 7add89aa9efbaf09f0a9dad0fdaabf61f3479183 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 11:48:39 +0200 Subject: [PATCH 71/78] fix: [singleView:jsonField] Render only first value --- .../element/genericElements/SingleViews/Fields/jsonField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/element/genericElements/SingleViews/Fields/jsonField.php b/templates/element/genericElements/SingleViews/Fields/jsonField.php index 52c0c32..ef14c58 100644 --- a/templates/element/genericElements/SingleViews/Fields/jsonField.php +++ b/templates/element/genericElements/SingleViews/Fields/jsonField.php @@ -4,7 +4,7 @@ $string = $field['raw']; } else { $value = Cake\Utility\Hash::extract($data, $field['path']); - $string = count($value) == 0 ? '' : $value; + $string = count($value) == 0 ? '' : $value[0]; } echo sprintf( '
    ', From 524dd10aaec6d19333c3fbd48baf0812c94b93c0 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 14:02:52 +0200 Subject: [PATCH 72/78] chg: [CRUD:add] Always allow UUID field to be set --- src/Controller/Component/CRUDComponent.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index e5ac732..8b77364 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -129,7 +129,8 @@ class CRUDComponent extends Component } if ($this->request->is('post')) { $patchEntityParams = [ - 'associated' => [] + 'associated' => [], + 'accessibleFields' => ['uuid' => true], ]; if (!empty($params['id'])) { unset($params['id']); From 31d04b8ff492900d397378c14fbb59666530a69a Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 14:04:01 +0200 Subject: [PATCH 73/78] chg: [individuals:capture] Prevent ID override and usage of _accessible property --- src/Model/Entity/Individual.php | 6 +++++- src/Model/Table/IndividualsTable.php | 31 ++++++++++++---------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Model/Entity/Individual.php b/src/Model/Entity/Individual.php index ef8e5db..471df36 100644 --- a/src/Model/Entity/Individual.php +++ b/src/Model/Entity/Individual.php @@ -7,5 +7,9 @@ use Cake\ORM\Entity; class Individual extends AppModel { - + protected $_accessible = [ + '*' => true, + 'id' => false, + 'uuid' => false, + ]; } diff --git a/src/Model/Table/IndividualsTable.php b/src/Model/Table/IndividualsTable.php index d0df577..79c6982 100644 --- a/src/Model/Table/IndividualsTable.php +++ b/src/Model/Table/IndividualsTable.php @@ -55,26 +55,21 @@ class IndividualsTable extends AppTable return null; } if (empty($existingIndividual)) { - $entity = $this->newEntity($individual, ['associated' => []]); - if (!$this->save($entity)) { - return null; - } - $individual = $entity; + $entity = $this->newEmptyEntity(); + $this->patchEntity($entity, $individual, [ + 'accessibleFields' => ['uuid' => true] + ]); + $entityToSave = $entity; } else { - $reserved = ['id', 'uuid', 'metaFields']; - foreach ($individual as $field => $value) { - if (in_array($field, $reserved)) { - continue; - } - $existingIndividual->$field = $value; - } - if (!$this->save($existingIndividual, ['associated' => false])) { - return null; - } - $individual = $existingIndividua; + $this->patchEntity($existingIndividual, $individual); + $entityToSave = $existingIndividual; } - $this->postCaptureActions($individual); - return $individual->id; + $savedEntity = $this->save($entityToSave, ['associated' => false]); + if (!$savedEntity) { + return null; + } + $this->postCaptureActions($savedEntity); + return $savedEntity->id; } public function postCaptureActions($individual): void From 15da5d067b7130761ad098dab5bfe34674ac7897 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 14:04:50 +0200 Subject: [PATCH 74/78] fix: [brood:captureIndividual] Typos preventing calls to correct functions --- src/Model/Table/BroodsTable.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index 0e3a2b0..c0a08b8 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -160,9 +160,9 @@ class BroodsTable extends AppTable } $response = $this->HTTPClientGET(sprintf('/individuals/view/%s/index.json', $individual_id), $brood); if ($response->isOk()) { - $org = $response->getJson(); - $this->Individual = TableRegistry::getTableLocator()->get('Individual'); - $result = $this->Individual->captureIndividual($individual); + $individual = $response->getJson(); + $this->Individuals = TableRegistry::getTableLocator()->get('Individuals'); + $result = $this->Individuals->captureIndividual($individual); return $result; } else { return false; From cf3a8653e18886329c00727b2dcd221a6c6c03a3 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 14:49:38 +0200 Subject: [PATCH 75/78] fix: [app] Prevent some ID overrides --- src/Controller/BroodsController.php | 21 +++++++++++ src/Controller/Component/CRUDComponent.php | 2 +- src/Model/Entity/Individual.php | 9 +++++ src/Model/Entity/Organisation.php | 13 +++++++ src/Model/Entity/SharingGroup.php | 28 +++++++++++++++ src/Model/Table/BroodsTable.php | 25 +++++++++++-- src/Model/Table/IndividualsTable.php | 7 ++-- src/Model/Table/OrganisationsTable.php | 34 +++++++----------- src/Model/Table/SharingGroupsTable.php | 41 ++++++++-------------- 9 files changed, 123 insertions(+), 57 deletions(-) diff --git a/src/Controller/BroodsController.php b/src/Controller/BroodsController.php index 1c0a11b..94afeda 100644 --- a/src/Controller/BroodsController.php +++ b/src/Controller/BroodsController.php @@ -156,6 +156,27 @@ class BroodsController extends AppController } } + public function downloadSharingGroup($brood_id, $sg_id) + { + $result = $this->Broods->downloadSharingGroup($brood_id, $sg_id, $this->ACL->getUser()['id']); + $success = __('Sharing group fetched from remote.'); + $fail = __('Could not save the remote sharing group'); + if ($this->ParamHandler->isRest()) { + if ($result) { + return $this->RestResponse->saveSuccessResponse('Brood', 'downloadSharingGroup', $brood_id, 'json', $success); + } else { + return $this->RestResponse->saveFailResponse('Brood', 'downloadSharingGroup', $brood_id, $fail, 'json'); + } + } else { + if ($result) { + $this->Flash->success($success); + } else { + $this->Flash->error($fail); + } + $this->redirect($this->referer()); + } + } + public function interconnectTools() { $this->InboxProcessors = TableRegistry::getTableLocator()->get('InboxProcessors'); diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 8b77364..86bb1ec 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -130,7 +130,7 @@ class CRUDComponent extends Component if ($this->request->is('post')) { $patchEntityParams = [ 'associated' => [], - 'accessibleFields' => ['uuid' => true], + 'accessibleFields' => $data->getAccessibleFieldForNew(), ]; if (!empty($params['id'])) { unset($params['id']); diff --git a/src/Model/Entity/Individual.php b/src/Model/Entity/Individual.php index 471df36..dbcb03c 100644 --- a/src/Model/Entity/Individual.php +++ b/src/Model/Entity/Individual.php @@ -12,4 +12,13 @@ class Individual extends AppModel 'id' => false, 'uuid' => false, ]; + + protected $_accessibleOnNew = [ + 'uuid' => true, + ]; + + public function getAccessibleFieldForNew(): array + { + return $this->_accessibleOnNew; + } } diff --git a/src/Model/Entity/Organisation.php b/src/Model/Entity/Organisation.php index 89fb838..3428c34 100644 --- a/src/Model/Entity/Organisation.php +++ b/src/Model/Entity/Organisation.php @@ -7,5 +7,18 @@ use Cake\ORM\Entity; class Organisation extends AppModel { + protected $_accessible = [ + '*' => true, + 'id' => false, + 'uuid' => false, + ]; + protected $_accessibleOnNew = [ + 'uuid' => true, + ]; + + public function getAccessibleFieldForNew(): array + { + return $this->_accessibleOnNew; + } } diff --git a/src/Model/Entity/SharingGroup.php b/src/Model/Entity/SharingGroup.php index e69de29..e47ba4a 100644 --- a/src/Model/Entity/SharingGroup.php +++ b/src/Model/Entity/SharingGroup.php @@ -0,0 +1,28 @@ + true, + 'id' => false, + 'uuid' => false, + 'organisation_id' => false, + 'user_id' => false, + ]; + + protected $_accessibleOnNew = [ + 'uuid' => true, + 'organisation_id' => true, + 'user_id' => true, + ]; + + public function getAccessibleFieldForNew(): array + { + return $this->_accessibleOnNew; + } +} diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index c0a08b8..4502a76 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -115,6 +115,7 @@ class BroodsTable extends AppTable } } + // TODO: Delete this function? public function downloadAndCapture($brood_id, $object_id, $scope, $path) { $query = $this->find(); @@ -122,7 +123,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $response = $this->HTTPClientGET(sprintf('/%s/view/%s/index.json', $scope, $org_id), $brood); + $response = $this->HTTPClientGET(sprintf('/%s/view/%s.json', $scope, $org_id), $brood); if ($response->isOk()) { $org = $response->getJson(); $this->Organisation = TableRegistry::getTableLocator()->get('Organisations'); @@ -140,7 +141,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $response = $this->HTTPClientGET(sprintf('/organisations/view/%s/index.json', $org_id), $brood); + $response = $this->HTTPClientGET(sprintf('/organisations/view/%s.json', $org_id), $brood); if ($response->isOk()) { $org = $response->getJson(); $this->Organisation = TableRegistry::getTableLocator()->get('Organisations'); @@ -158,7 +159,7 @@ class BroodsTable extends AppTable if (empty($brood)) { throw new NotFoundException(__('Brood not found')); } - $response = $this->HTTPClientGET(sprintf('/individuals/view/%s/index.json', $individual_id), $brood); + $response = $this->HTTPClientGET(sprintf('/individuals/view/%s.json', $individual_id), $brood); if ($response->isOk()) { $individual = $response->getJson(); $this->Individuals = TableRegistry::getTableLocator()->get('Individuals'); @@ -169,6 +170,24 @@ class BroodsTable extends AppTable } } + public function downloadSharingGroup($brood_id, $sg_id, $user_id) + { + $query = $this->find(); + $brood = $query->where(['id' => $brood_id])->first(); + if (empty($brood)) { + throw new NotFoundException(__('Brood not found')); + } + $response = $this->HTTPClientGET(sprintf('/sharing-groups/view/%s.json', $sg_id), $brood); + if ($response->isOk()) { + $individual = $response->getJson(); + $this->SharingGroups = TableRegistry::getTableLocator()->get('SharingGroups'); + $result = $this->SharingGroups->captureSharingGroup($individual, $user_id); + return $result; + } else { + return false; + } + } + public function queryLocalTools($brood_id) { $query = $this->find(); diff --git a/src/Model/Table/IndividualsTable.php b/src/Model/Table/IndividualsTable.php index 79c6982..da73d92 100644 --- a/src/Model/Table/IndividualsTable.php +++ b/src/Model/Table/IndividualsTable.php @@ -55,11 +55,10 @@ class IndividualsTable extends AppTable return null; } if (empty($existingIndividual)) { - $entity = $this->newEmptyEntity(); - $this->patchEntity($entity, $individual, [ - 'accessibleFields' => ['uuid' => true] + $entityToSave = $this->newEmptyEntity(); + $this->patchEntity($entityToSave, $individual, [ + 'accessibleFields' => $entityToSave->getAccessibleFieldForNew() ]); - $entityToSave = $entity; } else { $this->patchEntity($existingIndividual, $individual); $entityToSave = $existingIndividual; diff --git a/src/Model/Table/OrganisationsTable.php b/src/Model/Table/OrganisationsTable.php index c23ee7b..8587864 100644 --- a/src/Model/Table/OrganisationsTable.php +++ b/src/Model/Table/OrganisationsTable.php @@ -55,9 +55,6 @@ class OrganisationsTable extends AppTable public function captureOrg($org): ?int { - if (!empty($org['id'])) { - unset($org['id']); - } if (!empty($org['uuid'])) { $existingOrg = $this->find()->where([ 'uuid' => $org['uuid'] @@ -66,27 +63,20 @@ class OrganisationsTable extends AppTable return null; } if (empty($existingOrg)) { - $data = $this->newEmptyEntity(); - $data = $this->patchEntity($data, $org, ['associated' => []]); - if (!$this->save($data)) { - return null; - } - $savedOrg = $data; + $entityToSave = $this->newEmptyEntity(); + $this->patchEntity($entityToSave, $org, [ + 'accessibleFields' => $entityToSave->getAccessibleFieldForNew() + ]); } else { - $reserved = ['id', 'uuid', 'metaFields']; - foreach ($org as $field => $value) { - if (in_array($field, $reserved)) { - continue; - } - $existingOrg->$field = $value; - } - if (!$this->save($existingOrg)) { - return null; - } - $savedOrg = $existingOrg; + $this->patchEntity($existingOrg, $org); + $entityToSave = $existingOrg; } - $this->postCaptureActions($savedOrg->id, $org); - return $savedOrg->id; + $savedEntity = $this->save($entityToSave, ['associated' => false]); + if (!$savedEntity) { + return null; + } + $this->postCaptureActions($savedEntity->id, $org); + return $savedEntity->id; } public function postCaptureActions($id, $org) diff --git a/src/Model/Table/SharingGroupsTable.php b/src/Model/Table/SharingGroupsTable.php index 41cf20c..6f18157 100644 --- a/src/Model/Table/SharingGroupsTable.php +++ b/src/Model/Table/SharingGroupsTable.php @@ -46,9 +46,6 @@ class SharingGroupsTable extends AppTable public function captureSharingGroup($input, int $user_id = 0): ?int { - if (!empty($input['id'])) { - unset($input['id']); - } if (!empty($input['uuid'])) { $existingSG = $this->find()->where([ 'uuid' => $input['uuid'] @@ -57,41 +54,31 @@ class SharingGroupsTable extends AppTable return null; } if (empty($existingSG)) { - $data = $this->newEmptyEntity(); + $entityToSave = $this->newEmptyEntity(); $input['organisation_id'] = $this->Organisations->captureOrg($input['organisation']); $input['user_id'] = $user_id; - $data = $this->patchEntity($data, $input, ['associated' => []]); - if (!$this->save($data)) { - return null; - } - $savedSG = $data; + $this->patchEntity($entityToSave, $input, [ + 'accessibleFields' => $entityToSave->getAccessibleFieldForNew() + ]); } else { - $reserved = ['id', 'uuid', 'metaFields']; - foreach ($input as $field => $value) { - if (in_array($field, $reserved)) { - continue; - } - $existingSG->$field = $value; - } - if (!$this->save($existingSG)) { - return null; - } - $savedSG = $existingSG; + $this->patchEntity($existingSG, $input); + $entityToSave = $existingSG; } - $this->postCaptureActions($savedSG->id, $input); - return $savedSG->id; + $savedEntity = $this->save($entityToSave, ['associated' => false]); + if (!$savedEntity) { + return null; + } + $this->postCaptureActions($savedEntity, $input); + return $savedEntity->id; } - public function postCaptureActions($id, $input): void + public function postCaptureActions($savedEntity, $input): void { - $sharingGroup = $this->find()->where([ - 'id' => $id - ])->first(); $orgs = []; foreach ($input['sharing_group_orgs'] as $sgo) { $organisation_id = $this->Organisations->captureOrg($sgo); $orgs[] = $this->SharingGroupOrgs->get($organisation_id); } - $this->SharingGroupOrgs->link($sharingGroup, $orgs); + $this->SharingGroupOrgs->link($savedEntity, $orgs); } } From 046c35851d01281bf853e3e3ead8098b17b7b46e Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 15:04:02 +0200 Subject: [PATCH 76/78] chg: [appModel] Moved getAccessibleFieldForNew function into appModel --- src/Model/Entity/AppModel.php | 5 ++++- src/Model/Entity/Individual.php | 5 ----- src/Model/Entity/Organisation.php | 5 ----- src/Model/Entity/SharingGroup.php | 5 ----- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Model/Entity/AppModel.php b/src/Model/Entity/AppModel.php index d4704fe..84c86b3 100644 --- a/src/Model/Entity/AppModel.php +++ b/src/Model/Entity/AppModel.php @@ -6,5 +6,8 @@ use Cake\ORM\Entity; class AppModel extends Entity { - + public function getAccessibleFieldForNew(): array + { + return $this->_accessibleOnNew ?? []; + } } diff --git a/src/Model/Entity/Individual.php b/src/Model/Entity/Individual.php index dbcb03c..a14e339 100644 --- a/src/Model/Entity/Individual.php +++ b/src/Model/Entity/Individual.php @@ -16,9 +16,4 @@ class Individual extends AppModel protected $_accessibleOnNew = [ 'uuid' => true, ]; - - public function getAccessibleFieldForNew(): array - { - return $this->_accessibleOnNew; - } } diff --git a/src/Model/Entity/Organisation.php b/src/Model/Entity/Organisation.php index 3428c34..bc78378 100644 --- a/src/Model/Entity/Organisation.php +++ b/src/Model/Entity/Organisation.php @@ -16,9 +16,4 @@ class Organisation extends AppModel protected $_accessibleOnNew = [ 'uuid' => true, ]; - - public function getAccessibleFieldForNew(): array - { - return $this->_accessibleOnNew; - } } diff --git a/src/Model/Entity/SharingGroup.php b/src/Model/Entity/SharingGroup.php index e47ba4a..34d4b01 100644 --- a/src/Model/Entity/SharingGroup.php +++ b/src/Model/Entity/SharingGroup.php @@ -20,9 +20,4 @@ class SharingGroup extends AppModel 'organisation_id' => true, 'user_id' => true, ]; - - public function getAccessibleFieldForNew(): array - { - return $this->_accessibleOnNew; - } } From 96658779e24dd6145fc8b4509a29dcef98effa76 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 20:41:36 +0200 Subject: [PATCH 77/78] chg: [metaFieldsTemplates:enisa-csirt-inventory] Added template --- .../meta_fields/enisa-csirt-inventory.json | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 libraries/default/meta_fields/enisa-csirt-inventory.json diff --git a/libraries/default/meta_fields/enisa-csirt-inventory.json b/libraries/default/meta_fields/enisa-csirt-inventory.json new file mode 100644 index 0000000..9171a5c --- /dev/null +++ b/libraries/default/meta_fields/enisa-csirt-inventory.json @@ -0,0 +1,74 @@ +{ + "description": "Template based on the ENISA's CSIRTs inventory", + "metaFields": [ + { + "field": "ISO 3166-1 Code", + "type": "text", + "regex": "[a-z]{2,3}" + }, + { + "field": "website", + "type": "text", + "regex": "(http(s)?:\\\/\\\/.)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&\/\/=]*)" + }, + { + "field": "enisa-geo-group", + "type": "text" + }, + { + "field": "is-approved", + "type": "boolean" + }, + { + "field": "first-member-type", + "type": "text" + }, + { + "field": "team-name", + "type": "text" + }, + { + "field": "oes-coverage", + "type": "text" + }, + { + "field": "enisa-tistatus", + "type": "text" + }, + { + "field": "csirt-network-status", + "type": "text" + }, + { + "field": "constituency", + "type": "text" + }, + { + "field": "establishment", + "type": "text" + }, + { + "field": "email", + "type": "text", + "regex": "(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" + }, + { + "field": "country-name", + "type": "text" + }, + { + "field": "short-team-name", + "type": "text" + }, + { + "field": "key", + "type": "text" + } + ], + "name": "ENISA CSIRT Network", + "namespace": "cnw", + "scope": "organisation", + "source": "enisa.europa.eu/topics/csirts-in-europe/csirt-inventory/certs-by-country-interactive-map", + "uuid": "089c68c7-d97e-4f21-a798-159cd10f7864", + "version": 1 +} \ No newline at end of file From 45de6f8bf54b254cdf72167607f8492cc0065439 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 28 Jun 2021 20:45:10 +0200 Subject: [PATCH 78/78] fix: [command:importer] Fixed variable typo --- src/Command/ImporterCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/ImporterCommand.php b/src/Command/ImporterCommand.php index 30cdc3d..c79ba76 100644 --- a/src/Command/ImporterCommand.php +++ b/src/Command/ImporterCommand.php @@ -161,7 +161,7 @@ class ImporterCommand extends Command 'valueField' => 'id' ])->where(['meta_template_id' => $metaTemplate->id])->toArray(); } else { - $this->io->error("Unkown template for UUID $metaTemplateUUID"); + $this->io->error("Unkown template for UUID {$config['metaTemplateUUID']}"); die(1); } }