chg: [requestProcessors] Usage of connector name, connector/user/broods validations & UI improvements

pull/59/head
mokaddem 2021-06-14 16:46:53 +02:00
parent 048842263a
commit 974ed02e9b
8 changed files with 167 additions and 63 deletions

View File

@ -1,6 +1,7 @@
<?php
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');
@ -30,9 +31,9 @@ class LocalToolRequestProcessor extends GenericRequestProcessor
return parent::create($requestData);
}
protected function assignProcessingTemplate($toolName)
protected function assignProcessingTemplate($connectorName)
{
$processingTemplatePath = sprintf('%s/%s/%s.php', $this->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,

View File

@ -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('<a href="%s" target="_blank">%s</a>',
$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('<a href="%s" target="_blank">%s</a>',
$this->Url->build(['controller' => 'broods', 'action' => 'view', $brood['id']]),
h($brood['name'])
);
}]
}],
['key' => 'individual', 'label' => __('Individual'), 'formatter' => function($individual, $row) {
return sprintf('<a href="%s" target="_blank">%s</a>',
$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('<div class="text-nowrap"><b>%s</b> @ <a href="%s" target="_blank">%s</a></div>',
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('<div class="py-2"><div>%s</div>%s</div><div class="d-none">
);
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('<div class="p-3">%s</div><div class="description-container">%s</div>',
$progress,

View File

@ -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'));
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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) {