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 ();
2022-07-13 15:38:51 +02:00
$this -> Security -> unlockedActions [] = 'checkGraph' ;
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-06-24 13:44:51 +02:00
if ( empty ( Configure :: read ( 'Plugin.Workflow_enable' ))) {
$requirementErrors [] = __ ( 'The workflow plugin must be enabled to use workflows. Go to `/servers/serverSettings/Plugin` the enable the `Plugin.Workflow` setting' );
$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 )
{
2022-07-13 10:42:43 +02:00
$filters = $this -> IndexFilter -> harvestParameters ([ 'format' ]);
if ( ! empty ( $filters [ 'format' ])) {
if ( $filters [ 'format' ] == 'dot' ) {
$dot = $this -> Workflow -> getDotNotation ( $id );
return $this -> RestResponse -> viewData ( $dot , $this -> response -> type ());
} else if ( $filters [ 'format' ] == 'mermaid' ) {
$mermaid = $this -> Workflow -> getMermaid ( $id );
return $this -> RestResponse -> viewData ( $mermaid , $this -> response -> type ());
}
}
2022-05-04 00:01:02 +02:00
$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 (
2022-06-24 11:22:26 +02:00
[ __ ( 'Could not create workflow for trigger %s' , $trigger_id ), $this -> Workflow -> validationErrors ],
2022-06-07 13:46:26 +02:00
null ,
'add' ,
$trigger_id ,
[ 'controller' => 'workflows' , 'action' => 'editor' ]
);
}
$workflow = $savedWorkflow ;
}
$modules = $this -> Workflow -> attachNotificationToModules ( $modules , $workflow );
2022-06-24 17:09:12 +02:00
$this -> loadModel ( 'WorkflowBlueprint' );
$workflowBlueprints = $this -> WorkflowBlueprint -> find ( 'all' );
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-24 17:09:12 +02:00
$this -> set ( 'workflowBlueprints' , $workflowBlueprints );
2022-05-04 00:01:02 +02:00
}
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-07-19 09:01:32 +02:00
$errorWhileLoading = $this -> Workflow -> getModuleLoadingError ();
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-22 09:45:03 +02:00
$filters = $this -> IndexFilter -> harvestParameters ([ 'type' , 'actiontype' , 'enabled' ]);
2022-06-07 13:46:26 +02:00
$moduleType = $filters [ 'type' ] ? ? 'action' ;
2022-06-22 09:45:03 +02:00
$actionType = $filters [ 'actiontype' ] ? ? 'all' ;
$enabledState = $filters [ 'enabled' ] ? ? false ;
2022-07-07 09:04:09 +02:00
if ( $moduleType == 'all' || $moduleType == 'custom' ) {
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-22 09:45:03 +02:00
if ( $actionType == 'mispmodule' ) {
$data = array_filter ( $data , function ( $module ) {
return ! empty ( $module [ 'is_misp_module' ]);
});
} else if ( $actionType == 'blocking' ) {
$data = array_filter ( $data , function ( $module ) {
return ! empty ( $module [ 'is_blocking' ]);
});
2022-07-07 09:04:09 +02:00
} else if ( $moduleType == 'custom' ) {
$data = array_filter ( $data , function ( $module ) {
return ! empty ( $module [ 'is_custom' ]);
});
2022-06-22 09:45:03 +02:00
}
if ( $enabledState !== false ) {
$moduleType = ! empty ( $enabledState ) ? 'enabled' : 'disabled' ;
$data = array_filter ( $data , function ( $module ) use ( $enabledState ) {
return ! empty ( $enabledState ) ? empty ( $module [ 'disabled' ]) : ! empty ( $module [ 'disabled' ]);
});
}
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 );
2022-06-22 09:45:03 +02:00
$this -> set ( 'actionType' , $actionType );
2022-07-19 09:01:32 +02:00
$this -> set ( 'errorWhileLoading' , $errorWhileLoading );
2022-06-01 08:22:26 +02:00
$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-07-07 09:28:28 +02:00
$module [ 'listening_workflows' ] = $this -> Workflow -> getListeningWorkflowForTrigger ( $module );
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
2022-07-13 15:38:51 +02:00
public function checkGraph ()
2022-05-20 17:34:41 +02:00
{
$this -> request -> allowMethod ([ 'post' ]);
2022-06-21 10:01:28 +02:00
$graphData = JsonTool :: decode ( $this -> request -> data [ 'graph' ]);
2022-05-20 17:34:41 +02:00
$cycles = [];
$isAcyclic = $this -> Workflow -> workflowGraphTool -> isAcyclic ( $graphData , $cycles );
2022-07-13 15:38:51 +02:00
$edges = [];
$hasMultipleOutputConnection = $this -> Workflow -> workflowGraphTool -> hasMultipleOutputConnection ( $graphData , $edges );
2022-05-20 17:34:41 +02:00
$data = [
2022-07-13 15:38:51 +02:00
'is_acyclic' => [
'is_acyclic' => $isAcyclic ,
'cycles' => $cycles ,
],
'multiple_output_connection' => [
'has_multiple_output_connection' => $hasMultipleOutputConnection ,
'edges' => $edges ,
]
2022-05-20 17:34:41 +02:00
];
return $this -> RestResponse -> viewData ( $data , 'json' );
}
2022-05-03 22:59:42 +02:00
}