diff --git a/docker-compose.yml b/docker-compose.yml index f8642281..064d4a42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,10 +47,10 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - - SHUFFLE_WORKER_VERSION=0.9.52 + - SHUFFLE_WORKER_VERSION=nightly - ORG_ID=${ORG_ID} - ENVIRONMENT_NAME=${ENVIRONMENT_NAME} - - BASE_URL=https://frikky-shuffle-5gvr4xx62w64-5001.githubpreview.dev + - BASE_URL=https://${OUTER_HOSTNAME}:${BACKEND_PORT} - DOCKER_API_VERSION=1.40 - SHUFFLE_BASE_IMAGE_NAME=${SHUFFLE_BASE_IMAGE_NAME} - SHUFFLE_BASE_IMAGE_REGISTRY=${SHUFFLE_BASE_IMAGE_REGISTRY} diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index e63ee93c..ef7384f7 100644 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -114,6 +114,10 @@ const AuthenticationOauth2 = (props) => { setButtonClicked(true); console.log("SCOPES: ", scopes); + client_id = client_id.trim() + client_secret = client_secret.trim() + oauth_url = oauth_url.trim() + var resources = ""; if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) { if (offlineAccess === true && !scopes.includes("offline_access")) { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 94a5def4..a3d11d88 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -14,6 +14,7 @@ import theme from '../theme'; import { Zoom, + Avatar, Popover, TextField, Drawer, @@ -45,8 +46,13 @@ import { Breadcrumbs, CircularProgress, Switch, + Chip, } from "@material-ui/core"; +import { + AvatarGroup, +} from "@mui/material" + import { OpenInNew as OpenInNewIcon, Undo as UndoIcon, @@ -248,6 +254,9 @@ const AngularWorkflow = (defaultprops) => { const [subworkflowStartnode, setSubworkflowStartnode] = React.useState(""); const [leftViewOpen, setLeftViewOpen] = React.useState(true); const [leftBarSize, setLeftBarSize] = React.useState(350); + const [creatorProfile, setCreatorProfile] = React.useState({}); + const [appGroup, setAppGroup] = React.useState([]); + const [triggerGroup, setTriggerGroup] = React.useState([]); const [executionText, setExecutionText] = React.useState(""); const [executionRequestStarted, setExecutionRequestStarted] = React.useState(false); @@ -1346,7 +1355,7 @@ const AngularWorkflow = (defaultprops) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for apps :O!"); + console.log("Status not 200 for app auth :O!"); } return response.json(); @@ -1460,6 +1469,10 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { + if (responseJson.success === false) { + return + } + // FIXME - handle versions on left bar //handleAppVersioning(responseJson) //var tmpapps = [] @@ -1495,7 +1508,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - alert.error(error.toString()); + alert.error("App loading error: ", error.toString()); }); }; @@ -1530,12 +1543,51 @@ const AngularWorkflow = (defaultprops) => { responseJson.errors = []; } + if (responseJson.actions === undefined || responseJson.actions === null) { + responseJson.actions = []; + } + + if (responseJson.triggers === undefined || responseJson.triggers === null) { + responseJson.triggers = []; + } + if (responseJson.public) { alert.info( - "This workflow is public. You will have to save it to make it your own!" + "This workflow is public. Save the workflow to " ); + + console.log("RESP: ", responseJson) + if (Object.getOwnPropertyNames(creatorProfile).length === 0) { + getUserProfile("frikky") + } + + //{appGroup.map((data, index) => { + //const [appGroup, setAppGroup] = React.useState([]); + var appsFound = [] + for (var key in responseJson.actions) { + const parsedAction = responseJson.actions[key] + if (parsedAction.large_image === undefined || parsedAction.large_image === null || parsedAction.large_image === "") { + continue + } + if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ + appsFound.push(parsedAction) + } + } + + setAppGroup(appsFound) + + appsFound = [] + for (var key in responseJson.triggers) { + const parsedAction = responseJson.triggers[key] + if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ + appsFound.push(parsedAction) + } + } + + setTriggerGroup(appsFound) } + // Appends SUBFLOWS. Does NOT run during normal grabbing of workflows. if (sourcenode.id !== undefined) { console.log("WORKFLOW: ", responseJson); @@ -2107,9 +2159,11 @@ const AngularWorkflow = (defaultprops) => { if (!lastSaved) { return unloadText; } else { - //document.removeEventListener("mousemove", onMouseUpdate, true); - document.removeEventListener("keydown", handleKeyDown, true); - document.removeEventListener("paste", handlePaste, true); + if (workflow.public === false) { + //document.removeEventListener("mousemove", onMouseUpdate, true); + document.removeEventListener("keydown", handleKeyDown, true); + document.removeEventListener("paste", handlePaste, true); + } } }); @@ -3393,7 +3447,7 @@ const AngularWorkflow = (defaultprops) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for apps :O!"); + console.log("Status not 200 for envs :O!"); if (isCloud) { setEnvironments([{ Name: "Cloud", Type: "cloud" }]); } else { @@ -3820,7 +3874,7 @@ const AngularWorkflow = (defaultprops) => { //var parentNode = cy.$("#" + event.target.data("id")); //if (parentNode.data("isButton") || parentNode.data("buttonId")) return; - if (nodedata.app_name !== undefined) { + if (nodedata.app_name !== undefined && !workflow.public === true) { const allNodes = cy.nodes().jsons(); var found = false; @@ -9893,6 +9947,10 @@ const AngularWorkflow = (defaultprops) => { }; const TopCytoscapeBar = (props) => { + if (workflow.public === true) { + return null + } + return (
@@ -9916,10 +9974,6 @@ const AngularWorkflow = (defaultprops) => {

{workflow.name}

- - {workflow.public ? ( -

Public Workflow PREVIEW

- ) : null}
@@ -10518,66 +10572,202 @@ const AngularWorkflow = (defaultprops) => { // executeWorkflowWebsocket() //}}>Execute websocket // + + const getUserProfile = (username) => { + fetch(`${globalUrl}/api/v1/users/creators/${username}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for WORKFLOW EXECUTION :O!"); + } - const leftView = leftViewOpen ? ( -
- -
- ) : ( -
-
{ - setLeftViewOpen(true); - setLeftBarSize(350); - }} - > - - - -
-
- ); - const executionPaperStyle = { - minWidth: "95%", - maxWidth: "95%", - marginTop: "5px", - color: "white", - marginBottom: 10, - padding: 5, - backgroundColor: surfaceColor, - cursor: "pointer", - display: "flex", - minHeight: 40, - maxHeight: 40, - }; + return response.json(); + }) + .then((responseJson) => { + console.log("FOUND USER FOR: ", username, responseJson) + if (responseJson.success !== false) { + setCreatorProfile(responseJson) + } + }) + .catch((error) => { + console.log(error); + }) + } + + const leftView = workflow.public === true ? +
+ + {workflow.name} + + + This workflow is public and { + saveWorkflow() + }}>must be saved to be used in your organization. + + {Object.getOwnPropertyNames(creatorProfile).length !== 0 && creatorProfile.github_avatar !== undefined && creatorProfile.github_avatar !== null ? +
+ { + }}> + + + + + + Shared by {creatorProfile.github_username} + +
+ : null} +
+ {workflow.tags !== undefined && workflow.tags !== null && workflow.tags.length > 0 ? +
+ + Tags + +
+ {workflow.tags.map((tag, index) => { + if (index >= 3) { + return null; + } - const parsedExecutionArgument = () => { - var showResult = executionData.execution_argument.trim(); - const validate = validateJson(showResult); + return ( + + ); + })} +
+
+ : null } +
+ + Mitre Att&ck: + + + TBD + +
+ {appGroup.length > 0 ? +
+ + Apps + + + {appGroup.map((data, index) => { + return ( + + + + ) + })} + +
+ : null} + {triggerGroup.length > 0 ? +
+ + Triggers + + + {triggerGroup.map((data, index) => { + return ( + + ) + })} + +
+ : null} +
+ + Related Workflows: + + + TBD + +
+ {workflow.description !== undefined && workflow.description !== null && workflow.description.length > 0 ? +
+ + Description + + + {workflow.description} + +
+ : null} +
+ : leftViewOpen ? ( +
+ +
+) : ( +
+
{ + setLeftViewOpen(true); + setLeftBarSize(350); + }} + > + + + +
+
+); - if (validate.valid) { - if (typeof validate.result === "string") { - try { - validate.result = JSON.parse(validate.result); - } catch (e) { - console.log("Error: ", e); - validate.valid = false; - } - } +const executionPaperStyle = { + minWidth: "95%", + maxWidth: "95%", + marginTop: "5px", + color: "white", + marginBottom: 10, + padding: 5, + backgroundColor: surfaceColor, + cursor: "pointer", + display: "flex", + minHeight: 40, + maxHeight: 40, +}; + +const parsedExecutionArgument = () => { + var showResult = executionData.execution_argument.trim(); + const validate = validateJson(showResult); + + if (validate.valid) { + if (typeof validate.result === "string") { + try { + validate.result = JSON.parse(validate.result); + } catch (e) { + console.log("Error: ", e); + validate.valid = false; + } + } return (
diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 91ac42da..d595b75a 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -352,7 +352,7 @@ const Docs = (defaultprops) => { } function Img(props) { - return {props.alt}; + return {props.alt}; } function CodeHandler(props) { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 5930dd1a..dc8b47e4 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -33,6 +33,10 @@ import { DialogContent, } from "@material-ui/core"; +import { + AvatarGroup, +} from "@mui/material" + import { GridOn as GridOnIcon, List as ListIcon, @@ -1310,6 +1314,30 @@ const Workflows = (props) => { ); }; + const getWorkflowAppgroup = (data) => { + if (data.actions === undefined || data.actions === null) { + return [] + } + + var appsFound = [] + for (var key in data.actions) { + const parsedAction = data.actions[key] + if (parsedAction.large_image === undefined || parsedAction.large_image === null || parsedAction.large_image === "") { + continue + } + + if (parsedAction.app_name === "Shuffle Tools" || parsedAction.app_id === "bc78f35c6c6351b07a09b7aed5d29652") { + continue + } + + if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ + appsFound.push(parsedAction) + } + } + + return appsFound + } + const WorkflowPaper = (props) => { const { data } = props; const [open, setOpen] = React.useState(false); @@ -1339,6 +1367,7 @@ const Workflows = (props) => { } const actions = data.actions !== null ? data.actions.length : 0; + const appGroup = getWorkflowAppgroup(data) const [triggers, subflows] = getWorkflowMeta(data); const workflowMenuButtons = ( @@ -1561,22 +1590,48 @@ const Workflows = (props) => { - - - - - {actions} - - - + {appGroup.length > 0 ? +
+ + {appGroup.map((data, index) => { + return ( +
{ + addFilter(data.app_name); + }} + > + + + +
+ ) + })} +
+
+ : + + + + + {actions} + + + + } { marginTop: 5, }} > - {data.tags !== undefined + {data.tags !== undefined && data.tags !== null ? data.tags.map((tag, index) => { if (index >= 3) { return null;