chg: [workflow] Moved directory of custom modules in `Lib` folder

pull/8530/head
Sami Mokaddem 2022-07-07 09:04:09 +02:00
parent aef257b4f7
commit a220cac1a5
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
9 changed files with 121 additions and 32 deletions

View File

@ -157,7 +157,7 @@ class WorkflowsController extends AppController
$moduleType = $filters['type'] ?? 'action';
$actionType = $filters['actiontype'] ?? 'all';
$enabledState = $filters['enabled'] ?? false;
if ($moduleType == 'all') {
if ($moduleType == 'all' || $moduleType == 'custom') {
$data = array_merge(
$modules["blocks_action"],
$modules["blocks_logic"]
@ -173,6 +173,10 @@ class WorkflowsController extends AppController
$data = array_filter($data, function ($module) {
return !empty($module['is_blocking']);
});
} else if ($moduleType == 'custom') {
$data = array_filter($data, function ($module) {
return !empty($module['is_custom']);
});
}
if ($enabledState !== false) {
$moduleType = !empty($enabledState) ? 'enabled' : 'disabled';

View File

@ -129,11 +129,14 @@ class GraphWalker
{
$outputs = ($node['outputs'] ?? []);
if ($node['data']['id'] == 'if') {
$useThenBranch = $this->_evaluateIFCondition($node, $roamingData);
return $useThenBranch ? ['output_1' => $outputs['output_1']] : ['output_2' => $outputs['output_2']];
$useFirstOutput = $this->_evaluateIFCondition($node, $roamingData);
return $useFirstOutput ? ['output_1' => $outputs['output_1']] : ['output_2' => $outputs['output_2']];
} else if ($node['data']['id'] == 'parallel-task') {
$this->_evaluateParallelTask($node, $roamingData, $outputs['output_1']);
return ['output_1' => []];
} else {
$useFirstOutput = $this->_evaluateCustomLogicCondition($node, $roamingData);
return $useFirstOutput ? ['output_1' => $outputs['output_1']] : ['output_2' => $outputs['output_2']];
}
return $outputs;
}
@ -144,6 +147,12 @@ class GraphWalker
return $result;
}
private function _evaluateCustomLogicCondition($node, WorkflowRoamingData $roamingData): bool
{
$result = $this->WorkflowModel->__executeNode($node, $roamingData);
return $result;
}
private function _evaluateParallelTask($parallel_node, WorkflowRoamingData $roamingData, array $connections)
{
foreach ($connections['connections'] as $connection) {

View File

@ -0,0 +1,23 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_blueprint_action_module extends WorkflowBaseModule
{
public $is_blocking = false;
public $disabled = true;
public $id = 'blueprint-action-module';
public $name = 'Blueprint action module';
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
public $icon = 'shapes';
public $inputs = 1;
public $outputs = 1;
public $params = [];
public function exec(array $node, WorkflowRoamingData $roamingData, array &$errors = []): bool
{
parent::exec($node, $roamingData, $errors);
// If $this->is_blocking == true, returning `false` will stop the execution.
$errors[] = __('Execution stopped');
return false;
}
}

View File

@ -0,0 +1,23 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_blueprint_logic_module extends WorkflowBaseLogicModule
{
public $disabled = true;
public $id = 'blueprint-logic-module';
public $name = 'Blueprint logic module';
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
public $icon = 'shapes';
public $inputs = 1;
public $outputs = 2;
public $params = [];
public function exec(array $node, WorkflowRoamingData $roamingData, array &$errors = []): bool
{
parent::exec($node, $roamingData, $errors);
$params = $this->getParamsWithValues($node);
$data = $roamingData->getData();
// Returning true will make the execution flow take the first output of this module. Otherwise, the second output will be used.
return true;
}
}

View File

@ -63,6 +63,7 @@ class Workflow extends AppModel
const CAPTURE_FIELDS = ['name', 'description', 'timestamp', 'data'];
const MODULE_ROOT_PATH = APP . 'Model/WorkflowModules/';
const CUSTOM_MODULE_ROOT_PATH = APP . 'Lib/WorkflowModules/';
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';
@ -149,7 +150,7 @@ 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'];
$module_config = $this->__getClassFromModuleFiles('trigger', [$filename], false)['classConfigs'];
return empty($module_config['disabled']);
}
@ -590,13 +591,26 @@ class Workflow extends AppModel
}
$phpModuleFiles = Workflow::__listPHPModuleFiles();
foreach ($phpModuleFiles as $type => $files) {
$classModuleFromFiles = $this->__getClassFromModuleFiles($type, $files);
if ($type == 'custom') {
continue;
}
$classModuleFromFiles = $this->__getClassFromModuleFiles($type, $files, false);
foreach ($classModuleFromFiles['classConfigs'] as $i => $config) {
$classModuleFromFiles['classConfigs'][$i]['module_type'] = $type;
}
$this->loaded_modules[$type] = $classModuleFromFiles['classConfigs'];
$this->loaded_classes[$type] = $classModuleFromFiles['instancedClasses'];
}
// Load custom PHP modules from Lib
foreach ($phpModuleFiles['custom'] as $type => $files) {
$classModuleFromFiles = $this->__getClassFromModuleFiles($type, $files, true);
foreach ($classModuleFromFiles['classConfigs'] as $i => $config) {
$classModuleFromFiles['classConfigs'][$i]['module_type'] = $type;
}
$this->loaded_modules[$type] = array_merge($this->loaded_modules[$type], $classModuleFromFiles['classConfigs']);
$this->loaded_classes[$type] = array_merge($this->loaded_classes[$type], $classModuleFromFiles['instancedClasses']);
}
// Load module from misp-module service
$modules_from_service = $this->__getModulesFromModuleService() ?? [];
$misp_module_class = $this->__getClassForMispModule($modules_from_service);
$misp_module_configs = [];
@ -703,24 +717,21 @@ class Workflow extends AppModel
$folder = new Folder(Workflow::MODULE_ROOT_PATH . $dir);
$filesInFolder = $folder->find('.*\.php', true);
$files[$dir] = array_diff($filesInFolder, ['..', '.']);
if ($dir == 'action') { // No custom module for the triggers
$customFolder = new Folder(Workflow::MODULE_ROOT_PATH . $dir . '/Custom');
if ($dir == 'action' || $dir == 'logic') { // No custom module for the triggers
$customFolder = new Folder(Workflow::CUSTOM_MODULE_ROOT_PATH . $dir);
$filesInCustomFolder = $customFolder->find('.*\.php', true);
$filesInCustomFolder = array_map(function($file) {
return 'Custom/' . $file;
}, $filesInCustomFolder);
$files[$dir] = array_merge($filesInFolder, array_diff($filesInCustomFolder, ['..', '.']));
$files['custom'][$dir] = array_diff($filesInCustomFolder, ['..', '.']);
}
}
return $files;
}
private function __getClassFromModuleFiles($type, $files)
private function __getClassFromModuleFiles($type, $files, $isCustom=false)
{
$instancedClasses = [];
$classConfigs = [];
foreach ($files as $filename) {
$filepath = sprintf('%s%s/%s', Workflow::MODULE_ROOT_PATH, $type, $filename);
$filepath = sprintf('%s%s/%s', (!empty($isCustom) ? Workflow::CUSTOM_MODULE_ROOT_PATH : Workflow::MODULE_ROOT_PATH), $type, $filename);
$instancedClass = $this->__getClassFromModuleFile($filepath);
if (is_string($instancedClass)) {
$this->__logLoadingError($filename, $instancedClass);
@ -731,6 +742,10 @@ class Workflow extends AppModel
}
$classConfigs[$instancedClass->id] = $instancedClass->getConfig();
$instancedClasses[$instancedClass->id] = $instancedClass;
if (!empty($isCustom)) {
$classConfigs[$instancedClass->id]['is_custom'] = true;
$instancedClasses[$instancedClass->id]->is_custom = true;
}
}
return [
'classConfigs' => $classConfigs,

View File

@ -3,6 +3,7 @@ class WorkflowBaseModule
{
public $is_misp_module = false;
public $is_blocking = false;
public $is_custom = false;
public $id = 'to-override';
public $name = 'to-override';
public $version = '0.1';
@ -175,4 +176,13 @@ class WorkflowBaseModule
class WorkflowBaseTriggerModule extends WorkflowBaseModule
{
public $blocking = false;
public $inputs = 0;
public $outputs = 1;
}
class WorkflowBaseLogicModule extends WorkflowBaseModule
{
public $blocking = false;
public $inputs = 1;
public $outputs = 2;
}

View File

@ -1,18 +0,0 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_custom_action extends WorkflowBaseModule
{
public $id = 'custom-action';
public $name = 'User-defined Module';
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
public $icon = 'hand-point-up';
public $inputs = 1;
public $outputs = 0;
public $params = [];
public function __construct()
{
parent::__construct();
}
}

View File

@ -38,8 +38,16 @@
'sort' => 'is_misp_module',
'data_path' => 'is_misp_module',
'element' => 'boolean',
'requirement' => $indexType == 'action',
'colors' => true,
'class' => 'short',
],
[
'name' => __('Custom'),
'sort' => 'is_custom',
'data_path' => 'is_custom',
'element' => 'boolean',
'colors' => true,
'class' => 'short',
],
[
'name' => __('Enabled'),
@ -86,11 +94,26 @@
'text' => __('misp-module'),
'active' => $indexType === 'action' && $actionType === 'mispmodule',
],
[
'url' => $baseurl . '/workflows/moduleIndex/type:custom',
'text' => __('Custom'),
'active' => $indexType === 'custom',
],
]
],
[
'type' => 'simple',
'children' => [
[
'url' => $baseurl . '/workflows/moduleIndex/actiontype:blocking',
'text' => __('Blocking'),
'active' => $indexType === 'action' && $actionType === 'blocking',
],
]
],
[
'type' => 'simple',
'children' => [
[
'url' => $baseurl . '/workflows/moduleIndex/type:all/enabled:1',
'text' => __('Enabled'),