mirror of https://github.com/MISP/MISP
new: [workflow] Added toggling module state
parent
4ffebbfff4
commit
574deccac8
|
|
@ -181,31 +181,54 @@ class WorkflowsController extends AppController
|
|||
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'view_module']);
|
||||
}
|
||||
|
||||
public function import()
|
||||
public function toggleModule($module_id, $enabled)
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$data = $this->request->data['Workflow'];
|
||||
$text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
|
||||
$workflow = JsonTool::decode($text);
|
||||
if ($workflow === null) {
|
||||
throw new MethodNotAllowedException(__('Error while decoding JSON'));
|
||||
}
|
||||
$workflow['Workflow']['enabled'] = false;
|
||||
$workflow['Workflow']['data'] = JsonTool::encode($workflow['Workflow']['data']);
|
||||
$this->request->data = $workflow;
|
||||
$this->add();
|
||||
$this->request->allowMethod(['post', 'put']);
|
||||
$saved = $this->Workflow->toggleModule($module_id, $enabled);
|
||||
if ($saved) {
|
||||
return $this->__getSuccessResponseBasedOnContext(
|
||||
__('%s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
|
||||
null,
|
||||
'toggle_module',
|
||||
$module_id,
|
||||
['action' => 'moduleIndex']
|
||||
);
|
||||
} else {
|
||||
return $this->__getFailResponseBasedOnContext(
|
||||
__('Could not %s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
|
||||
null,
|
||||
'toggle_module',
|
||||
$module_id,
|
||||
['action' => 'moduleIndex']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function export($id)
|
||||
{
|
||||
$workflow = $this->Workflow->fetchWorkflow($id);
|
||||
$content = JsonTool::encode($workflow, JSON_PRETTY_PRINT);
|
||||
$this->response->body($content);
|
||||
$this->response->type('json');
|
||||
$this->response->download(sprintf('workflow_%s_%s.json', $workflow['Workflow']['name'], time()));
|
||||
return $this->response;
|
||||
}
|
||||
// public function import()
|
||||
// {
|
||||
// if ($this->request->is('post') || $this->request->is('put')) {
|
||||
// $data = $this->request->data['Workflow'];
|
||||
// $text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
|
||||
// $workflow = JsonTool::decode($text);
|
||||
// if ($workflow === null) {
|
||||
// throw new MethodNotAllowedException(__('Error while decoding JSON'));
|
||||
// }
|
||||
// $workflow['Workflow']['enabled'] = false;
|
||||
// $workflow['Workflow']['data'] = JsonTool::encode($workflow['Workflow']['data']);
|
||||
// $this->request->data = $workflow;
|
||||
// $this->add();
|
||||
// }
|
||||
// }
|
||||
|
||||
// public function export($id)
|
||||
// {
|
||||
// $workflow = $this->Workflow->fetchWorkflow($id);
|
||||
// $content = JsonTool::encode($workflow, JSON_PRETTY_PRINT);
|
||||
// $this->response->body($content);
|
||||
// $this->response->type('json');
|
||||
// $this->response->download(sprintf('workflow_%s_%s.json', $workflow['Workflow']['name'], time()));
|
||||
// return $this->response;
|
||||
// }
|
||||
|
||||
private function __getSuccessResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7045,6 +7045,13 @@ class Server extends AppModel
|
|||
'test' => 'testForPortNumber',
|
||||
'type' => 'numeric'
|
||||
),
|
||||
'WorkflowTriggers_publish' => array(
|
||||
'level' => 1,
|
||||
'description' => __('Enable/disable the `publish` trigger'),
|
||||
'value' => false,
|
||||
'test' => 'testBool',
|
||||
'type' => 'boolean'
|
||||
),
|
||||
'Cortex_services_url' => array(
|
||||
'level' => 1,
|
||||
'description' => __('The url used to access Cortex. By default, it is accessible at http://cortex-url'),
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ class Workflow extends AppModel
|
|||
const REDIS_KEY_WORKFLOW_NAMESPACE = 'workflow';
|
||||
const REDIS_KEY_WORKFLOW_PER_TRIGGER = 'workflow:workflow_list:%s';
|
||||
const REDIS_KEY_TRIGGER_PER_WORKFLOW = 'workflow:trigger_list:%s';
|
||||
const REDIS_KEY_MODULES_ENABLED = 'workflow:modules_enabled';
|
||||
|
||||
const BLOCKING_PATH = 'blocking';
|
||||
const NON_BLOCKING_PATH = 'non-blocking';
|
||||
|
|
@ -136,8 +137,34 @@ class Workflow extends AppModel
|
|||
{
|
||||
$filename = sprintf('Module_%s.php', preg_replace('/[^a-zA-Z0-9_]/', '_', Inflector::underscore($trigger_id)));
|
||||
$module_config = $this->__getClassFromModuleFiles('trigger', [$filename])['classConfigs'];
|
||||
// FIXME: Merge global configuration!
|
||||
return empty($module_config['disabled']);
|
||||
$module_disabled = empty(Configure::read(sprintf('Plugin.WorkflowTriggers_%s', $trigger_id)));
|
||||
return empty($module_config['disabled']) && !$module_disabled;
|
||||
}
|
||||
|
||||
protected function getEnabledModules(): array
|
||||
{
|
||||
try {
|
||||
$redis = $this->setupRedisWithException();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
$list = $redis->sMembers(Workflow::REDIS_KEY_MODULES_ENABLED);
|
||||
return !empty($list) ? $list : [];
|
||||
}
|
||||
|
||||
public function toggleModule($module_id, $enable): bool
|
||||
{
|
||||
try {
|
||||
$redis = $this->setupRedisWithException();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
if ($enable) {
|
||||
$list = $redis->sAdd(Workflow::REDIS_KEY_MODULES_ENABLED, $module_id);
|
||||
} else {
|
||||
$list = $redis->sRem(Workflow::REDIS_KEY_MODULES_ENABLED, $module_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkTriggerListenedTo($trigger_id)
|
||||
|
|
@ -465,6 +492,17 @@ class Workflow extends AppModel
|
|||
'warning' => [],
|
||||
'info' => [],
|
||||
];
|
||||
if ($module['disabled']) {
|
||||
$modules[$moduleType][$i]['notifications']['error'][] = [
|
||||
'text' => __('Module disabled'),
|
||||
'description' => __('This module is disabled and thus will not be executed.'),
|
||||
'details' => [
|
||||
__('Disabled modules that are blocking will also block the remaining of the execution')
|
||||
],
|
||||
'__show_in_sidebar' => false,
|
||||
'__show_in_node' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $modules;
|
||||
|
|
@ -500,24 +538,36 @@ class Workflow extends AppModel
|
|||
private function __mergeGlobalConfigIntoLoadedModules()
|
||||
{
|
||||
/* FIXME: Delete `disabled` entry. This is for testing while we wait for module settings */
|
||||
array_walk($this->loaded_modules['trigger'], function (&$trigger) {
|
||||
$module_enabled = !in_array($trigger['id'], ['publish', 'new-attribute']);
|
||||
$trigger['html_template'] = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
|
||||
$trigger['disabled'] = $module_enabled;
|
||||
$this->loaded_classes['trigger'][$trigger['id']]->disabled = $module_enabled;
|
||||
$this->loaded_classes['trigger'][$trigger['id']]->html_template = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
|
||||
});
|
||||
array_walk($this->loaded_modules['logic'], function (&$logic) {
|
||||
$module_enabled = true;
|
||||
$logic['disabled'] = !$module_enabled;
|
||||
$this->loaded_classes['logic'][$logic['id']]->disabled = $module_enabled;
|
||||
});
|
||||
array_walk($this->loaded_modules['action'], function (&$action) {
|
||||
$module_enabled = !in_array($action['id'], ['push-zmq', 'slack-message', 'mattermost-message', 'add-tag', 'writeactions', 'enrich-event', 'testaction', 'stop-execution', ]);
|
||||
$action['disabled'] = $module_enabled;
|
||||
$this->loaded_classes['action'][$action['id']]->disabled = $module_enabled;
|
||||
});
|
||||
// array_walk($this->loaded_modules['logic'], function (&$logic) {
|
||||
// $module_enabled = true;
|
||||
// $logic['disabled'] = !$module_enabled;
|
||||
// $this->loaded_classes['logic'][$logic['id']]->disabled = $module_enabled;
|
||||
// });
|
||||
// array_walk($this->loaded_modules['action'], function (&$action) {
|
||||
// $module_enabled = !in_array($action['id'], ['push-zmq', 'slack-message', 'mattermost-message', 'add-tag', 'writeactions', 'enrich-event', 'testaction', 'stop-execution', ]);
|
||||
// $action['disabled'] = $module_enabled;
|
||||
// $this->loaded_classes['action'][$action['id']]->disabled = $module_enabled;
|
||||
// });
|
||||
/* FIXME: end */
|
||||
foreach ($this->loaded_modules['trigger'] as &$trigger) {
|
||||
$module_disabled = empty(Configure::read(sprintf('Plugin.WorkflowTriggers_%s', $trigger['id'])));
|
||||
$trigger['html_template'] = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
|
||||
$trigger['disabled'] = $module_disabled;
|
||||
$this->loaded_classes['trigger'][$trigger['id']]->disabled = $module_disabled;
|
||||
$this->loaded_classes['trigger'][$trigger['id']]->html_template = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
|
||||
}
|
||||
$enabledModules = $this->getEnabledModules();
|
||||
array_walk($this->loaded_modules['logic'], function (&$logic) use ($enabledModules) {
|
||||
$module_disabled = !in_array($logic['id'], $enabledModules);
|
||||
$logic['disabled'] = $module_disabled;
|
||||
$this->loaded_classes['logic'][$logic['id']]->disabled = $module_disabled;
|
||||
});
|
||||
array_walk($this->loaded_modules['action'], function (&$action) use ($enabledModules) {
|
||||
$module_disabled = !in_array($action['id'], $enabledModules);
|
||||
$action['disabled'] = $module_disabled;
|
||||
$this->loaded_classes['action'][$action['id']]->disabled = $module_disabled;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function __getEnabledModulesFromModuleService()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ class Module_parallel_task extends WorkflowBaseModule
|
|||
public $html_template = 'parallel';
|
||||
public $params = [];
|
||||
|
||||
private $Workflow;
|
||||
private $Job;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ $classFromSeverity = [
|
|||
'error' => 'danger',
|
||||
];
|
||||
?>
|
||||
<div id="<?= h($block['id']) ?>" class="sidebar-workflow-block" style="user-select: none;" data-blockid="<?= h($block['id']) ?>">
|
||||
<div id="<?= h($block['id']) ?>" class="sidebar-workflow-block" style="user-select: none;" data-blockid="<?= h($block['id']) ?>" title="<?= !empty($block['disabled']) ? __('This module is disabled') : '' ?>">
|
||||
<div class="icon">
|
||||
<?php if (!empty($block['icon'])) : ?>
|
||||
<i class="<?= $this->FontAwesome->getClass($block['icon']) ?> fa-fw <?= $block['icon_class'] ?? '' ?>"></i>
|
||||
|
|
@ -24,8 +24,13 @@ $classFromSeverity = [
|
|||
</span>
|
||||
<span class="block-notification-container">
|
||||
<?php foreach (array_keys($classFromSeverity) as $severity) : ?>
|
||||
<?php if (!empty($block['notifications'][$severity])) : ?>
|
||||
<button class="btn btn-mini btn-<?= $classFromSeverity[$severity] ?>" type="button" title="<?= implode('
', h(Hash::extract($block['notifications'][$severity], '{n}.text'))) ?>" onclick="showNotificationModalForSidebarModule(this)">
|
||||
<?php
|
||||
$visibleNotifications = array_filter($block['notifications'][$severity], function($notification) {
|
||||
return $notification['__show_in_sidebar'];
|
||||
});
|
||||
?>
|
||||
<?php if (!empty($visibleNotifications)) : ?>
|
||||
<button class="btn btn-mini btn-<?= $classFromSeverity[$severity] ?>" type="button" title="<?= implode('
', h(Hash::extract($visibleNotifications, '{n}.text'))) ?>" onclick="showNotificationModalForSidebarModule(this)">
|
||||
<?php if ($severity == 'danger') : ?>
|
||||
<i class="<?= $this->FontAwesome->getClass('times-circle') ?>"></i>
|
||||
<?php elseif ($severity == 'warning') : ?>
|
||||
|
|
@ -34,7 +39,7 @@ $classFromSeverity = [
|
|||
<i class="<?= $this->FontAwesome->getClass('exclamation-circle') ?>"></i>
|
||||
<?php endif; ?>
|
||||
<strong>
|
||||
<?= count($block['notifications'][$severity]) ?>
|
||||
<?= count($visibleNotifications) ?>
|
||||
</strong>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@
|
|||
}
|
||||
$url .= '/' . $url_params_values;
|
||||
}
|
||||
if (!empty($action['url_suffix'])) {
|
||||
$url .= $action['url_suffix'];
|
||||
}
|
||||
if (!empty($action['url_extension'])) {
|
||||
$url .= '.' . $action['url_extension'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,8 +98,9 @@
|
|||
'title' => __('Enable'),
|
||||
'icon' => 'play',
|
||||
'postLink' => true,
|
||||
'url' => $baseurl . '/workflows/enableModule',
|
||||
'url' => $baseurl . '/workflows/toggleModule',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'url_suffix' => '/1',
|
||||
'postLinkConfirm' => __('Are you sure you want to enable this module?'),
|
||||
'complex_requirement' => array(
|
||||
'function' => function ($row, $options) use ($isSiteAdmin) {
|
||||
|
|
@ -116,8 +117,9 @@
|
|||
'title' => __('Disable'),
|
||||
'icon' => 'stop',
|
||||
'postLink' => true,
|
||||
'url' => $baseurl . '/workflows/disableModule',
|
||||
'url' => $baseurl . '/workflows/toggleModule',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'url_suffix' => '/0',
|
||||
'postLinkConfirm' => __('Are you sure you want to disable this module?'),
|
||||
'complex_requirement' => array(
|
||||
'function' => function ($row, $options) use ($isSiteAdmin) {
|
||||
|
|
|
|||
|
|
@ -538,6 +538,9 @@ function loadWorkflow(workflow) {
|
|||
if (block.data.module_type == 'logic') {
|
||||
blockClass.push('block-type-logic')
|
||||
}
|
||||
if (block.data.disabled) {
|
||||
blockClass.push('disabled')
|
||||
}
|
||||
var html = getTemplateForBlock(block.data)
|
||||
editor.nodeId = block.id // force the editor to use the saved id of the block instead of generating a new one
|
||||
editor.addNode(
|
||||
|
|
@ -1056,8 +1059,10 @@ function genBlockNotificationHtml(block) {
|
|||
var html = ''
|
||||
var $notificationContainer = $('<span></span>')
|
||||
severities.forEach(function(severity) {
|
||||
if (module.notifications[severity] && module.notifications[severity].length > 0) {
|
||||
var notificationTitles = module.notifications[severity].map(function (notification) {
|
||||
var visibleNotifications = module.notifications[severity].filter(function (notification) { return notification
|
||||
.__show_in_node})
|
||||
if (visibleNotifications && visibleNotifications.length > 0) {
|
||||
var notificationTitles = visibleNotifications.map(function (notification) {
|
||||
return notification.text
|
||||
}).join('
')
|
||||
var $notification = $('<button class="btn btn-mini" role="button" onclick="showNotificationModalForBlock(this)"></button>')
|
||||
|
|
@ -1072,7 +1077,7 @@ function genBlockNotificationHtml(block) {
|
|||
})
|
||||
.append(
|
||||
$('<i class="fas"></i>').addClass(iconBySeverity[severity]),
|
||||
$('<strong></strong>').text(' '+module.notifications[severity].length)
|
||||
$('<strong></strong>').text(' '+visibleNotifications.length)
|
||||
)
|
||||
$notificationContainer.append($notification)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue