2022-05-03 22:59:42 +02:00
|
|
|
<?php
|
|
|
|
App::uses('AppController', 'Controller');
|
|
|
|
|
|
|
|
class WorkflowsController extends AppController
|
|
|
|
{
|
|
|
|
public $components = array(
|
2022-05-04 10:44:18 +02:00
|
|
|
'RequestHandler'
|
2022-05-03 22:59:42 +02:00
|
|
|
);
|
|
|
|
|
2022-05-20 17:34:41 +02:00
|
|
|
public function beforeFilter()
|
|
|
|
{
|
|
|
|
parent::beforeFilter();
|
|
|
|
$this->Security->unlockedActions[] = 'hasAcyclicGraph';
|
2022-06-13 10:48:59 +02:00
|
|
|
$requirementErrors = [];
|
|
|
|
if (empty(Configure::read('MISP.background_jobs'))) {
|
|
|
|
$requirementErrors[] = __('Background workers must be enabled to use workflows');
|
|
|
|
$this->render('error');
|
|
|
|
}
|
2022-05-24 14:11:49 +02:00
|
|
|
try {
|
2022-05-24 15:24:13 +02:00
|
|
|
$this->Workflow->setupRedisWithException();
|
2022-05-24 14:11:49 +02:00
|
|
|
} catch (Exception $e) {
|
2022-06-13 10:48:59 +02:00
|
|
|
$requirementErrors[] = $e->getMessage();
|
|
|
|
}
|
|
|
|
if (!empty($requirementErrors)) {
|
|
|
|
$this->set('requirementErrors', $requirementErrors);
|
2022-05-24 14:11:49 +02:00
|
|
|
$this->render('error');
|
|
|
|
}
|
2022-05-20 17:34:41 +02:00
|
|
|
}
|
|
|
|
|
2022-05-03 22:59:42 +02:00
|
|
|
public function index()
|
|
|
|
{
|
2022-05-04 00:01:02 +02:00
|
|
|
$params = [
|
|
|
|
'filters' => ['name', 'uuid'],
|
2022-05-04 10:44:18 +02:00
|
|
|
'quickFilters' => ['name', 'uuid'],
|
2022-05-04 00:01:02 +02:00
|
|
|
];
|
|
|
|
$this->CRUD->index($params);
|
|
|
|
if ($this->IndexFilter->isRest()) {
|
|
|
|
return $this->restResponsePayload;
|
|
|
|
}
|
|
|
|
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'index'));
|
2022-05-03 22:59:42 +02:00
|
|
|
}
|
|
|
|
|
2022-05-30 10:53:13 +02:00
|
|
|
public function rebuildRedis()
|
|
|
|
{
|
2022-06-07 13:46:26 +02:00
|
|
|
$this->Workflow->rebuildRedis();
|
2022-05-26 11:20:59 +02:00
|
|
|
}
|
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
public function edit($id)
|
2022-05-03 22:59:42 +02:00
|
|
|
{
|
2022-05-04 00:01:02 +02:00
|
|
|
$this->set('id', $id);
|
2022-06-07 13:46:26 +02:00
|
|
|
$savedWorkflow = $this->Workflow->fetchWorkflow($id);
|
2022-05-04 13:54:55 +02:00
|
|
|
if ($this->request->is('post') || $this->request->is('put')) {
|
|
|
|
$newWorkflow = $this->request->data;
|
2022-05-04 15:30:02 +02:00
|
|
|
$newWorkflow['Workflow']['data'] = JsonTool::decode($newWorkflow['Workflow']['data']);
|
2022-05-04 13:54:55 +02:00
|
|
|
$newWorkflow = $this->__applyDataFromSavedWorkflow($newWorkflow, $savedWorkflow);
|
2022-06-07 13:46:26 +02:00
|
|
|
$errors = $this->Workflow->editWorkflow($newWorkflow);
|
2022-05-04 13:54:55 +02:00
|
|
|
$redirectTarget = ['action' => 'view', $id];
|
|
|
|
if (!empty($errors)) {
|
|
|
|
return $this->__getFailResponseBasedOnContext($errors, null, 'edit', $this->Workflow->id, $redirectTarget);
|
|
|
|
} else {
|
|
|
|
$successMessage = __('Workflow saved.');
|
2022-06-07 13:46:26 +02:00
|
|
|
$savedWorkflow =$this->Workflow->fetchWorkflow($id);
|
2022-05-04 13:54:55 +02:00
|
|
|
return $this->__getSuccessResponseBasedOnContext($successMessage, $savedWorkflow, 'edit', false, $redirectTarget);
|
|
|
|
}
|
|
|
|
} else {
|
2022-05-16 14:43:54 +02:00
|
|
|
$savedWorkflow['Workflow']['data'] = JsonTool::encode($savedWorkflow['Workflow']['data']);
|
2022-05-04 13:54:55 +02:00
|
|
|
$this->request->data = $savedWorkflow;
|
2022-05-04 00:01:02 +02:00
|
|
|
}
|
2022-05-03 22:59:42 +02:00
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'edit'));
|
|
|
|
$this->render('add');
|
2022-05-03 22:59:42 +02:00
|
|
|
}
|
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
public function delete($id)
|
2022-05-03 22:59:42 +02:00
|
|
|
{
|
2022-05-04 00:01:02 +02:00
|
|
|
$params = [
|
|
|
|
];
|
|
|
|
$this->CRUD->delete($id, $params);
|
|
|
|
if ($this->IndexFilter->isRest()) {
|
|
|
|
return $this->restResponsePayload;
|
|
|
|
}
|
|
|
|
}
|
2022-05-03 22:59:42 +02:00
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
public function view($id)
|
|
|
|
{
|
|
|
|
$this->CRUD->view($id, [
|
|
|
|
]);
|
|
|
|
if ($this->IndexFilter->isRest()) {
|
|
|
|
return $this->restResponsePayload;
|
|
|
|
}
|
|
|
|
$this->set('id', $id);
|
|
|
|
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'view'));
|
2022-05-03 22:59:42 +02:00
|
|
|
}
|
|
|
|
|
2022-06-07 13:46:26 +02:00
|
|
|
public function editor($trigger_id)
|
2022-05-03 22:59:42 +02:00
|
|
|
{
|
2022-05-21 10:16:58 +02:00
|
|
|
$modules = $this->Workflow->getModulesByType();
|
2022-06-07 13:46:26 +02:00
|
|
|
$trigger_ids = Hash::extract($modules['blocks_trigger'], '{n}.id');
|
|
|
|
if (!in_array($trigger_id, $trigger_ids)) {
|
|
|
|
return $this->__getFailResponseBasedOnContext(
|
|
|
|
[__('Unkown trigger %s', $trigger_id)],
|
|
|
|
null,
|
|
|
|
'add',
|
|
|
|
$trigger_id,
|
|
|
|
['controller' => 'workflows', 'action' => 'triggers']
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$workflow = $this->Workflow->fetchWorkflowByTrigger($trigger_id, false);
|
|
|
|
if (empty($workflow)) { // Workflow do not exists yet. Create it.
|
|
|
|
$this->Workflow->create();
|
|
|
|
$savedWorkflow = $this->Workflow->save([
|
|
|
|
'name' => sprintf('Workflow for trigger %s', $trigger_id),
|
|
|
|
'trigger_id' => $trigger_id,
|
|
|
|
]);
|
|
|
|
if (empty($savedWorkflow)) {
|
|
|
|
return $this->__getFailResponseBasedOnContext(
|
|
|
|
[__('Could not create workflow for trigger %s', $trigger_id), $this->validationErrors],
|
|
|
|
null,
|
|
|
|
'add',
|
|
|
|
$trigger_id,
|
|
|
|
['controller' => 'workflows', 'action' => 'editor']
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$workflow = $savedWorkflow;
|
|
|
|
}
|
|
|
|
$modules = $this->Workflow->attachNotificationToModules($modules, $workflow);
|
2022-05-05 18:02:04 +02:00
|
|
|
$this->set('selectedWorkflow', $workflow);
|
2022-05-04 00:01:02 +02:00
|
|
|
$this->set('modules', $modules);
|
|
|
|
}
|
|
|
|
|
2022-06-07 13:46:26 +02:00
|
|
|
public function triggers()
|
|
|
|
{
|
|
|
|
$triggers = $this->Workflow->getModulesByType('trigger');
|
|
|
|
$triggers = $this->Workflow->attachWorkflowToTriggers($triggers);
|
|
|
|
$data = $triggers;
|
|
|
|
if ($this->_isRest()) {
|
|
|
|
return $this->RestResponse->viewData($data, $this->response->type());
|
|
|
|
}
|
|
|
|
$this->set('data', $data);
|
|
|
|
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'index_trigger']);
|
|
|
|
}
|
|
|
|
|
2022-06-01 08:22:26 +02:00
|
|
|
public function moduleIndex()
|
2022-05-16 14:44:54 +02:00
|
|
|
{
|
2022-05-21 10:16:58 +02:00
|
|
|
$modules = $this->Workflow->getModulesByType();
|
2022-06-01 11:49:51 +02:00
|
|
|
$this->Module = ClassRegistry::init('Module');
|
2022-06-07 13:46:26 +02:00
|
|
|
$mispModules = $this->Module->getModules('Action');
|
2022-06-01 11:49:51 +02:00
|
|
|
$this->set('module_service_error', !is_array($mispModules));
|
2022-06-01 08:22:26 +02:00
|
|
|
$filters = $this->IndexFilter->harvestParameters(['type']);
|
2022-06-07 13:46:26 +02:00
|
|
|
$moduleType = $filters['type'] ?? 'action';
|
|
|
|
if ($moduleType == 'all') {
|
2022-06-01 08:22:26 +02:00
|
|
|
$data = array_merge(
|
2022-06-07 13:46:26 +02:00
|
|
|
$modules["blocks_action"],
|
|
|
|
$modules["blocks_logic"]
|
2022-06-01 08:22:26 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$data = $modules["blocks_{$moduleType}"];
|
|
|
|
}
|
2022-06-01 09:24:20 +02:00
|
|
|
if ($this->_isRest()) {
|
|
|
|
return $this->RestResponse->viewData($data, $this->response->type());
|
|
|
|
}
|
2022-06-01 08:22:26 +02:00
|
|
|
$this->set('data', $data);
|
|
|
|
$this->set('indexType', $moduleType);
|
|
|
|
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'index_module']);
|
2022-05-16 14:44:54 +02:00
|
|
|
}
|
|
|
|
|
2022-06-01 08:22:26 +02:00
|
|
|
public function moduleView($module_id)
|
2022-05-17 08:37:37 +02:00
|
|
|
{
|
2022-06-01 08:22:26 +02:00
|
|
|
$module = $this->Workflow->getModuleByID($module_id);
|
|
|
|
if (empty($module)) {
|
2022-05-17 08:37:37 +02:00
|
|
|
throw new NotFoundException(__('Invalid trigger ID'));
|
|
|
|
}
|
2022-06-07 13:46:26 +02:00
|
|
|
$is_trigger = $module['module_type'] == 'trigger';
|
|
|
|
if ($is_trigger) {
|
|
|
|
$module = $this->Workflow->attachWorkflowToTriggers([$module])[0];
|
2022-06-01 08:22:26 +02:00
|
|
|
}
|
2022-06-01 09:24:20 +02:00
|
|
|
if ($this->_isRest()) {
|
|
|
|
return $this->RestResponse->viewData($module, $this->response->type());
|
|
|
|
}
|
2022-06-01 08:22:26 +02:00
|
|
|
$this->set('data', $module);
|
|
|
|
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'view_module']);
|
2022-05-17 08:37:37 +02:00
|
|
|
}
|
|
|
|
|
2022-06-17 10:06:11 +02:00
|
|
|
public function toggleModule($module_id, $enabled, $is_trigger=false)
|
2022-05-30 10:10:46 +02:00
|
|
|
{
|
2022-06-17 09:20:27 +02:00
|
|
|
$this->request->allowMethod(['post', 'put']);
|
2022-06-17 10:06:11 +02:00
|
|
|
$saved = $this->Workflow->toggleModule($module_id, $enabled, $is_trigger);
|
2022-06-17 09:20:27 +02:00
|
|
|
if ($saved) {
|
|
|
|
return $this->__getSuccessResponseBasedOnContext(
|
|
|
|
__('%s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
|
|
|
|
null,
|
|
|
|
'toggle_module',
|
|
|
|
$module_id,
|
2022-06-17 10:06:11 +02:00
|
|
|
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
|
2022-06-17 09:20:27 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return $this->__getFailResponseBasedOnContext(
|
|
|
|
__('Could not %s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
|
|
|
|
null,
|
|
|
|
'toggle_module',
|
|
|
|
$module_id,
|
2022-06-17 10:06:11 +02:00
|
|
|
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
|
2022-06-17 09:20:27 +02:00
|
|
|
);
|
2022-05-30 10:10:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
private function __getSuccessResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
|
|
|
|
{
|
|
|
|
if ($this->_isRest()) {
|
|
|
|
if (!is_null($data)) {
|
|
|
|
return $this->RestResponse->viewData($data, $this->response->type());
|
|
|
|
} else {
|
2022-05-04 13:54:55 +02:00
|
|
|
return $this->RestResponse->saveSuccessResponse('Workflow', $action, $id, false, $message);
|
2022-05-04 00:01:02 +02:00
|
|
|
}
|
|
|
|
} elseif ($this->request->is('ajax')) {
|
2022-05-04 13:54:55 +02:00
|
|
|
return $this->RestResponse->saveSuccessResponse('Workflow', $action, $id, false, $message, $data);
|
2022-05-04 00:01:02 +02:00
|
|
|
} else {
|
|
|
|
$this->Flash->success($message);
|
|
|
|
$this->redirect($redirect);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2022-05-03 22:59:42 +02:00
|
|
|
|
2022-05-04 00:01:02 +02:00
|
|
|
private function __getFailResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
|
|
|
|
{
|
|
|
|
if (is_array($message)) {
|
|
|
|
$message = implode(', ', $message);
|
|
|
|
}
|
|
|
|
if ($this->_isRest()) {
|
|
|
|
if ($data !== null) {
|
|
|
|
return $this->RestResponse->viewData($data, $this->response->type());
|
|
|
|
} else {
|
2022-05-04 13:54:55 +02:00
|
|
|
return $this->RestResponse->saveFailResponse('Workflow', $action, $id, $message);
|
2022-05-04 00:01:02 +02:00
|
|
|
}
|
|
|
|
} elseif ($this->request->is('ajax')) {
|
2022-05-04 13:54:55 +02:00
|
|
|
return $this->RestResponse->saveFailResponse('Workflow', $action, $id, $message, false, $data);
|
2022-05-04 00:01:02 +02:00
|
|
|
} else {
|
|
|
|
$this->Flash->error($message);
|
2022-06-07 13:46:26 +02:00
|
|
|
$this->redirect($redirect);
|
2022-05-04 00:01:02 +02:00
|
|
|
}
|
2022-05-03 22:59:42 +02:00
|
|
|
}
|
2022-05-04 13:54:55 +02:00
|
|
|
|
|
|
|
private function __applyDataFromSavedWorkflow($newWorkflow, $savedWorkflow)
|
|
|
|
{
|
|
|
|
if (!isset($newReport['Workflow'])) {
|
|
|
|
$newReport = ['Workflow' => $newWorkflow];
|
|
|
|
}
|
2022-06-07 13:46:26 +02:00
|
|
|
$ignoreFieldList = ['id', 'uuid'];
|
2022-05-04 13:54:55 +02:00
|
|
|
foreach (Workflow::CAPTURE_FIELDS as $field) {
|
|
|
|
if (!in_array($field, $ignoreFieldList) && isset($newWorkflow['Workflow'][$field])) {
|
|
|
|
$savedWorkflow['Workflow'][$field] = $newWorkflow['Workflow'][$field];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $savedWorkflow;
|
|
|
|
}
|
2022-05-20 17:34:41 +02:00
|
|
|
|
|
|
|
public function hasAcyclicGraph()
|
|
|
|
{
|
|
|
|
$this->request->allowMethod(['post']);
|
|
|
|
$graphData = $this->request->data;
|
|
|
|
$cycles = [];
|
|
|
|
$isAcyclic = $this->Workflow->workflowGraphTool->isAcyclic($graphData, $cycles);
|
|
|
|
$data = [
|
|
|
|
'is_acyclic' => $isAcyclic,
|
|
|
|
'cycles' => $cycles,
|
|
|
|
];
|
|
|
|
return $this->RestResponse->viewData($data, 'json');
|
|
|
|
}
|
2022-05-03 22:59:42 +02:00
|
|
|
}
|