From 7e0228c69e9a467eb9f44f9e57fe47f3650ae43c Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 11 Nov 2022 14:46:11 +0100 Subject: [PATCH] Added automatic starting of schedules and better tracking to NEW workflow onboardings --- frontend/src/components/ConfigureWorkflow.jsx | 419 +++++++++++++++--- 1 file changed, 366 insertions(+), 53 deletions(-) diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 66c48ab2..09fb5560 100644 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from "react"; +import { useInterval } from "react-powerhooks"; import { InputAdornment, @@ -15,7 +16,11 @@ import { ListItemText, Fade, } from "@material-ui/core"; -import { FavoriteBorder as FavoriteBorderIcon } from "@material-ui/icons"; +import { + FavoriteBorder as FavoriteBorderIcon, + Error as ErrorIcon, + CheckCircleRounded as CheckCircleRoundedIcon, +} from "@mui/icons-material"; import { FixName } from "../views/Apps.jsx"; // Handles workflow updates on first open to highlight the issues of the workflow @@ -45,6 +50,8 @@ const ConfigureWorkflow = (props) => { setAuthenticationType, alert, showTriggers, + workflowExecutions, + getWorkflowExecution, } = props; const [requiredActions, setRequiredActions] = React.useState([]); const [requiredVariables, setRequiredVariables] = React.useState([]); @@ -54,6 +61,22 @@ const ConfigureWorkflow = (props) => { const [firstLoad, setFirstLoad] = React.useState(""); const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false); + const [checkStarted, setCheckStarted] = React.useState(false); + + const { start, stop } = useInterval({ + duration: 3000, + startImmediate: false, + callback: () => { + if (getWorkflowExecution !== undefined && workflowExecutions !== undefined) { + const paramkey = workflow.id + getWorkflowExecution(paramkey) + } else { + console.log("Executions or getWorkflowExecutions not defined") + } + }, + }); + + // Where is this from? if (workflow === undefined || workflow === null) { return null; } @@ -118,6 +141,8 @@ const ConfigureWorkflow = (props) => { action: action, update_version: action.app_version, app: {}, + steps: [], + show_steps: false, }; const app = apps.find( @@ -126,17 +151,21 @@ const ConfigureWorkflow = (props) => { (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version))) - ); + ) + + //newaction.steps = wazuhSteps if (app === undefined || app === null) { - //console.log("App not found: ", action.app_name); - const subapp = apps.find(app => app.name === action.app_name) if (subapp !== undefined && subapp !== null) { newaction.update_version = "1.1.0" } - newaction.must_activate = true; + newaction.steps.push({ + "title": "Activate app", + "type": "activate", + "required": true, + }) } else { if ( action.authentication_id === "" && @@ -157,6 +186,12 @@ const ConfigureWorkflow = (props) => { } } + newaction.steps.push({ + "title": "Authenticate app", + "type": "authenticate", + "required": true, + }) + if (!filled) { newaction.must_authenticate = true; newaction.action_ids.push(action.id); @@ -244,6 +279,38 @@ const ConfigureWorkflow = (props) => { var trigger = workflow.triggers[key]; trigger.index = key; + if (trigger.trigger_type === "WEBHOOK") { + console.log("Found webhook: ", trigger) + if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") { + console.log("Actions: ", newactions) + const findapp = trigger.app_association.name.toLowerCase() + const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp) + + // Adding webhook to start of it + if (foundindex >= 0) { + const tmpsteps = newactions[foundindex].steps + newactions[foundindex].steps = [ + { + "title": "Configure Webhook", + "type": "webhook", + "required": true, + } + ] + + for (var subkey in tmpsteps) { + newactions[foundindex].steps.push(tmpsteps[subkey]) + } + + newactions[foundindex].show_steps = true + + console.log("CHANGED ACTION: ", newactions[foundindex]) + //console.log("Index: ", newactions[foundindex]) + + continue + } + } + } + if (trigger.status === "running") { continue; } @@ -297,7 +364,7 @@ const ConfigureWorkflow = (props) => { } const TriggerSection = (props) => { - const { trigger } = props; + const { trigger } = props return ( @@ -527,8 +594,8 @@ const ConfigureWorkflow = (props) => { textTransform: "none", textAlign: "left", justifyContent: "flex-start", - backgroundColor: action.auth_done ? theme.palette.surfaceColor : "#ffffff", - color: action.auth_done ? "#686a6c" : "#2f2f2f", + backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor, + color: action.auth_done ? "#686a6c" : "#ffffff", borderRadius: theme.palette.borderRadius, minWidth: 350, maxHeight: 50, @@ -571,46 +638,11 @@ const ConfigureWorkflow = (props) => { src={action.large_image} /> - {action.auth_done ? "Authenticated" : `Authenticate ${action.app_name}`} + {action.auth_done ? "Authenticated" : `Authenticate ${action.app_name.replaceAll("_", " ")}`} : null} - {action.must_activate ? ( - - ) : null} - {action.update_version !== action.app_version ? ( + {action.update_version !== action.app_version ? - ) : null} + : + action.must_activate ? + + : + null + } ); } @@ -667,10 +737,245 @@ const ConfigureWorkflow = (props) => { // Based on the color here. Default: #f86a3e //backgroundColor: selectedUsecaseCategory === usecase.name ? usecase.color : theme.palette.surfaceColor, + const BoxHighlight = (props) => { + const {data, appname, appinfo, index, activeStep, setActiveStep, finished, } = props + + const [hovered, setHovered] = useState(false) + const [isOpen, setIsOpen] = useState(false) + const [isLoading, setIsLoading] = useState(false) + + // This kind of just works for new workflows.. + // What if we try many times? + + var webhook = { + "name": "Testhook", + "description": `A Webhook Trigger has been started and is ready to receive events from ${appname}. Click to copy the URL to send events to.`, + "url": "", + } + + useEffect(() => { + if (data.type === "webhook" && !finished) { + if (!checkStarted) { + setCheckStarted(true) + start() + } + } + }, []) + + // Load webhook docs from the app itself (Wazuh) + // Add a "sample" for what the event is supposed to look like + // Have a listener for when ACTUALLY is received + // INJECT the URL into the documentation when loading it in + // How can we load it in? Should we just use the app name & get docs -> parse? + if (data.type === "webhook" && workflow.triggers !== undefined && workflow.triggers !== null) { + //console.log("Find webhook in the workflow!") + for (var key in workflow.triggers) { + if (workflow.triggers[key].trigger_type !== "WEBHOOK") { + continue + } + + for (var subkey in workflow.triggers[key].parameters) { + const param = workflow.triggers[key].parameters[subkey] + if (param.name === "url") { + webhook.url = param.value + + if (isLoading === false) { + setIsLoading(true) + } + break + } + } + } + } else if (data.type == "authenticate") { + //console.log("Handle app authentication in the workflow!") + } + + return ( +
{ + setIsOpen(!isOpen) + setActiveStep(index) + }} + onMouseOver={() => { + setHovered(true); + }} + onMouseOut={() => { + setHovered(false); + }} + > + +
+ {data.title} + {finished ? + + : + + } +
+ + {activeStep === index ? +
+ {data.type === "webhook" ? +
{ + event.preventDefault() + console.log("Clicked Webhook") + + var copyText = document.getElementById("copy_element_shuffle") + if (copyText !== undefined && copyText !== null) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + alert.error("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(webhook.url); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999 + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + alert.success("Copied Webhook URL"); + } + }}> + {webhook.description} + {/*{webhook.url}*/} + {isLoading && finished === false ? +
+ +
+ : + null + } +
+ : + + } +
+ : null} + +
+ ) + } + + const AppWrapper = (props) => { + const {data, parentindex} = props + const [clicked, setClicked] = useState(true) + const [hovered, setHovered] = useState(false) + const [activeStep, setActiveStep] = useState(0) + const [firstRun, setFirstRun] = useState(true) + const [finishCount, setFinishCount] = useState(0) + + return ( +
+
{ + //setClicked(!clicked) + }} + onMouseOver={() => { + setHovered(true); + }} + onMouseOut={() => { + setHovered(false); + }} + > + + {data.label} + + + Configure {data.app_name.replaceAll("_", " ")} + +
+ {clicked === true ? + data.steps.map((step, index) => { + var finished = false + if (step.type === "activate") { + if (data.activation_done === true) { + finished = true + + if (index === activeStep && firstRun === true) { + setActiveStep(activeStep+1) + } + + if (firstRun) { + setFinishCount(finishCount+1) + } + } + } + + if (step.type === "authenticate") { + console.log("AUTH STEP: ", step) + if (data.must_authenticate === true ) { + finished = false + } else { + if (data.activation_done === true && data.auth_done === true) { + finished = true + + if (firstRun) { + setFinishCount(finishCount+1) + } + + if (index === activeStep && firstRun === true) { + setActiveStep(activeStep+1) + } + } + } + } + + if (step.type === "webhook") { + for (var key in workflowExecutions) { + const exec = workflowExecutions[key] + if (exec.execution_argument !== undefined && exec.execution_argument !== null && exec.execution_argument.length > 0 && exec.execution_source === "webhook") { + //console.log("Done: ", exec) + + finished = true + if (index === activeStep && firstRun === true) { + setActiveStep(activeStep+1) + + } + + if (firstRun) { + setFinishCount(finishCount+1) + } + + // Finished + source = webhook + + stop() + //if (isLoading === true) { + // setIsLoading(false) + //} + + + break + } + } + } + + if (firstRun === true && index === data.steps.length-1) { + setFirstRun(false) + } + + return ( + + ) + }) + : null} +
+ ) + } + const topColor = "#f86a3e, #fc3922" return (
-
+
{workflow.name} @@ -679,13 +984,20 @@ const ConfigureWorkflow = (props) => { {requiredActions.length > 0 ? ( - + Required Actions + {requiredActions.map((data, index) => { return ( - +
+ {data.steps !== undefined && data.steps !== null && data.show_steps === true ? + + : + + } +
) })}
@@ -743,6 +1055,7 @@ const ConfigureWorkflow = (props) => { variant={"outlined"} style={{}} onClick={() => { + stop() setShowFinalizeAnimation(true) setTimeout(() => { if (itemChanged) {