Added fixes for public workflows with app visuals

This commit is contained in:
frikky
2022-01-31 12:22:37 +01:00
parent cc2610c2cb
commit a811036fb2
5 changed files with 337 additions and 88 deletions
+2 -2
View File
@@ -47,10 +47,10 @@ services:
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
environment: environment:
- SHUFFLE_WORKER_VERSION=0.9.52 - SHUFFLE_WORKER_VERSION=nightly
- ORG_ID=${ORG_ID} - ORG_ID=${ORG_ID}
- ENVIRONMENT_NAME=${ENVIRONMENT_NAME} - 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 - DOCKER_API_VERSION=1.40
- SHUFFLE_BASE_IMAGE_NAME=${SHUFFLE_BASE_IMAGE_NAME} - SHUFFLE_BASE_IMAGE_NAME=${SHUFFLE_BASE_IMAGE_NAME}
- SHUFFLE_BASE_IMAGE_REGISTRY=${SHUFFLE_BASE_IMAGE_REGISTRY} - SHUFFLE_BASE_IMAGE_REGISTRY=${SHUFFLE_BASE_IMAGE_REGISTRY}
+4
View File
@@ -114,6 +114,10 @@ const AuthenticationOauth2 = (props) => {
setButtonClicked(true); setButtonClicked(true);
console.log("SCOPES: ", scopes); console.log("SCOPES: ", scopes);
client_id = client_id.trim()
client_secret = client_secret.trim()
oauth_url = oauth_url.trim()
var resources = ""; var resources = "";
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) { if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
if (offlineAccess === true && !scopes.includes("offline_access")) { if (offlineAccess === true && !scopes.includes("offline_access")) {
+200 -10
View File
@@ -14,6 +14,7 @@ import theme from '../theme';
import { import {
Zoom, Zoom,
Avatar,
Popover, Popover,
TextField, TextField,
Drawer, Drawer,
@@ -45,8 +46,13 @@ import {
Breadcrumbs, Breadcrumbs,
CircularProgress, CircularProgress,
Switch, Switch,
Chip,
} from "@material-ui/core"; } from "@material-ui/core";
import {
AvatarGroup,
} from "@mui/material"
import { import {
OpenInNew as OpenInNewIcon, OpenInNew as OpenInNewIcon,
Undo as UndoIcon, Undo as UndoIcon,
@@ -248,6 +254,9 @@ const AngularWorkflow = (defaultprops) => {
const [subworkflowStartnode, setSubworkflowStartnode] = React.useState(""); const [subworkflowStartnode, setSubworkflowStartnode] = React.useState("");
const [leftViewOpen, setLeftViewOpen] = React.useState(true); const [leftViewOpen, setLeftViewOpen] = React.useState(true);
const [leftBarSize, setLeftBarSize] = React.useState(350); 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 [executionText, setExecutionText] = React.useState("");
const [executionRequestStarted, setExecutionRequestStarted] = const [executionRequestStarted, setExecutionRequestStarted] =
React.useState(false); React.useState(false);
@@ -1346,7 +1355,7 @@ const AngularWorkflow = (defaultprops) => {
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for apps :O!"); console.log("Status not 200 for app auth :O!");
} }
return response.json(); return response.json();
@@ -1460,6 +1469,10 @@ const AngularWorkflow = (defaultprops) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) {
return
}
// FIXME - handle versions on left bar // FIXME - handle versions on left bar
//handleAppVersioning(responseJson) //handleAppVersioning(responseJson)
//var tmpapps = [] //var tmpapps = []
@@ -1495,7 +1508,7 @@ const AngularWorkflow = (defaultprops) => {
} }
}) })
.catch((error) => { .catch((error) => {
alert.error(error.toString()); alert.error("App loading error: ", error.toString());
}); });
}; };
@@ -1530,12 +1543,51 @@ const AngularWorkflow = (defaultprops) => {
responseJson.errors = []; responseJson.errors = [];
} }
if (responseJson.actions === undefined || responseJson.actions === null) {
responseJson.actions = [];
}
if (responseJson.triggers === undefined || responseJson.triggers === null) {
responseJson.triggers = [];
}
if (responseJson.public) { if (responseJson.public) {
alert.info( 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. // Appends SUBFLOWS. Does NOT run during normal grabbing of workflows.
if (sourcenode.id !== undefined) { if (sourcenode.id !== undefined) {
console.log("WORKFLOW: ", responseJson); console.log("WORKFLOW: ", responseJson);
@@ -2107,10 +2159,12 @@ const AngularWorkflow = (defaultprops) => {
if (!lastSaved) { if (!lastSaved) {
return unloadText; return unloadText;
} else { } else {
if (workflow.public === false) {
//document.removeEventListener("mousemove", onMouseUpdate, true); //document.removeEventListener("mousemove", onMouseUpdate, true);
document.removeEventListener("keydown", handleKeyDown, true); document.removeEventListener("keydown", handleKeyDown, true);
document.removeEventListener("paste", handlePaste, true); document.removeEventListener("paste", handlePaste, true);
} }
}
}); });
// Nodeselectbatching: // Nodeselectbatching:
@@ -3393,7 +3447,7 @@ const AngularWorkflow = (defaultprops) => {
}) })
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Status not 200 for apps :O!"); console.log("Status not 200 for envs :O!");
if (isCloud) { if (isCloud) {
setEnvironments([{ Name: "Cloud", Type: "cloud" }]); setEnvironments([{ Name: "Cloud", Type: "cloud" }]);
} else { } else {
@@ -3820,7 +3874,7 @@ const AngularWorkflow = (defaultprops) => {
//var parentNode = cy.$("#" + event.target.data("id")); //var parentNode = cy.$("#" + event.target.data("id"));
//if (parentNode.data("isButton") || parentNode.data("buttonId")) return; //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(); const allNodes = cy.nodes().jsons();
var found = false; var found = false;
@@ -9893,6 +9947,10 @@ const AngularWorkflow = (defaultprops) => {
}; };
const TopCytoscapeBar = (props) => { const TopCytoscapeBar = (props) => {
if (workflow.public === true) {
return null
}
return ( return (
<div style={topBarStyle}> <div style={topBarStyle}>
<div style={{ margin: "0px 10px 0px 10px" }}> <div style={{ margin: "0px 10px 0px 10px" }}>
@@ -9916,10 +9974,6 @@ const AngularWorkflow = (defaultprops) => {
</h2> </h2>
</Link> </Link>
<h2 style={{ margin: 0 }}>{workflow.name}</h2> <h2 style={{ margin: 0 }}>{workflow.name}</h2>
{workflow.public ? (
<h2 style={{ margin: 0 }}>Public Workflow PREVIEW</h2>
) : null}
</Breadcrumbs> </Breadcrumbs>
</div> </div>
</div> </div>
@@ -10519,7 +10573,143 @@ const AngularWorkflow = (defaultprops) => {
//}}>Execute websocket</Button> //}}>Execute websocket</Button>
// //
const leftView = leftViewOpen ? ( 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!");
}
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 ?
<div style={{minHeight: "80vh", height: "100%", minWidth: leftBarSize-70, maxWidth: leftBarSize-70, zIndex: 0, padding: 35, borderRight: "1px solid rgba(91,96,100,1)",}}>
<Typography variant="h6" color="textPrimary" style={{
margin: "0px 0px 0px 0px",
}}
>
{workflow.name}
</Typography>
<Typography variant="body2" color="textSecondary">
This workflow is public and <span style={{color: "#f86a3e", cursor: "pointer", }} onClick={() => {
saveWorkflow()
}}>must be saved</span> to be used in your organization.
</Typography>
{Object.getOwnPropertyNames(creatorProfile).length !== 0 && creatorProfile.github_avatar !== undefined && creatorProfile.github_avatar !== null ?
<div style={{display: "flex", marginTop: 10, }}>
<IconButton color="primary" style={{padding: 0, marginRight: 10, }} aria-controls="simple-menu" aria-haspopup="true" onClick={(event) => {
}}>
<Link to={`/creators/${creatorProfile.github_username}`} style={{textDecoration: "none", color: "#f86a3e"}}>
<Avatar style={{height: 30, width: 30,}} alt={"Workflow creator"} src={creatorProfile.github_avatar} />
</Link>
</IconButton>
<Typography variant="body1" color="textSecondary" style={{color: ""}}>
Shared by <Link to={`/creators/${creatorProfile.github_username}`} style={{textDecoration: "none", color: "#f86a3e"}}>{creatorProfile.github_username}</Link>
</Typography>
</div>
: null}
<div style={{marginTop: 15}} />
{workflow.tags !== undefined && workflow.tags !== null && workflow.tags.length > 0 ?
<div style={{display: "flex", overflow: "hidden",marginTop: 5, }}>
<Typography variant="body1" style={{marginRight: 10, }}>
Tags
</Typography>
<div style={{display: "flex"}}>
{workflow.tags.map((tag, index) => {
if (index >= 3) {
return null;
}
return (
<Chip
key={index}
style={{backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",}}
label={tag}
variant="outlined"
color="primary"
/>
);
})}
</div>
</div>
: null }
<div style={{display: "flex", marginTop: 10, }}>
<Typography variant="body1">
Mitre Att&ck:
</Typography>
<Typography variant="body1" color="textSecondary">
TBD
</Typography>
</div>
{appGroup.length > 0 ?
<div style={{display: "flex", marginTop: 10, }}>
<Typography variant="body1">
Apps
</Typography>
<AvatarGroup max={6} style={{marginLeft: 10, }}>
{appGroup.map((data, index) => {
return (
<Link to={`/apps/${data.app_id}`}>
<Avatar alt={data.app_name} src={data.large_image} style={{width: 30, height: 30}}/>
</Link>
)
})}
</AvatarGroup>
</div>
: null}
{triggerGroup.length > 0 ?
<div style={{display: "flex", marginTop: 10, }}>
<Typography variant="body1">
Triggers
</Typography>
<AvatarGroup max={6} style={{marginLeft: 10, }}>
{triggerGroup.map((data, index) => {
return (
<Avatar alt={data.app_name} src={data.large_image} style={{width: 30, height: 30}}/>
)
})}
</AvatarGroup>
</div>
: null}
<div style={{display: "flex", marginTop: 10, }}>
<Typography variant="body1">
Related Workflows:
</Typography>
<Typography variant="body1" color="textSecondary">
TBD
</Typography>
</div>
{workflow.description !== undefined && workflow.description !== null && workflow.description.length > 0 ?
<div style={{marginTop: 5, }}>
<Typography variant="body1">
Description
</Typography>
<Typography variant="body1" color="textSecondary">
{workflow.description}
</Typography>
</div>
: null}
</div>
: leftViewOpen ? (
<div <div
style={{ style={{
minWidth: leftBarSize, minWidth: leftBarSize,
+1 -1
View File
@@ -352,7 +352,7 @@ const Docs = (defaultprops) => {
} }
function Img(props) { function Img(props) {
return <img style={{ maxWidth: "100%" }} alt={props.alt} src={props.src} />; return <img style={{ borderRadius: theme.palette.borderRadius, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
} }
function CodeHandler(props) { function CodeHandler(props) {
+56 -1
View File
@@ -33,6 +33,10 @@ import {
DialogContent, DialogContent,
} from "@material-ui/core"; } from "@material-ui/core";
import {
AvatarGroup,
} from "@mui/material"
import { import {
GridOn as GridOnIcon, GridOn as GridOnIcon,
List as ListIcon, 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 WorkflowPaper = (props) => {
const { data } = props; const { data } = props;
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
@@ -1339,6 +1367,7 @@ const Workflows = (props) => {
} }
const actions = data.actions !== null ? data.actions.length : 0; const actions = data.actions !== null ? data.actions.length : 0;
const appGroup = getWorkflowAppgroup(data)
const [triggers, subflows] = getWorkflowMeta(data); const [triggers, subflows] = getWorkflowMeta(data);
const workflowMenuButtons = ( const workflowMenuButtons = (
@@ -1561,6 +1590,31 @@ const Workflows = (props) => {
</Tooltip> </Tooltip>
</Grid> </Grid>
<Grid item style={workflowActionStyle}> <Grid item style={workflowActionStyle}>
{appGroup.length > 0 ?
<div style={{display: "flex", marginTop: 8, }}>
<AvatarGroup max={4} style={{marginLeft: 5, maxHeight: 24,}}>
{appGroup.map((data, index) => {
return (
<div
style={{
height: 24,
width: 24,
filter: "brightness(0.6)",
cursor: "pointer",
}}
onClick={() => {
addFilter(data.app_name);
}}
>
<Tooltip color="primary" title={data.app_name} placement="bottom">
<Avatar alt={data.app_name} src={data.large_image} style={{width: 24, height: 24}}/>
</Tooltip>
</div>
)
})}
</AvatarGroup>
</div>
:
<Tooltip color="primary" title="Action amount" placement="bottom"> <Tooltip color="primary" title="Action amount" placement="bottom">
<span style={{ color: "#979797", display: "flex" }}> <span style={{ color: "#979797", display: "flex" }}>
<BubbleChartIcon <BubbleChartIcon
@@ -1577,6 +1631,7 @@ const Workflows = (props) => {
</Typography> </Typography>
</span> </span>
</Tooltip> </Tooltip>
}
<Tooltip <Tooltip
color="primary" color="primary"
title="Trigger amount" title="Trigger amount"
@@ -1681,7 +1736,7 @@ const Workflows = (props) => {
marginTop: 5, marginTop: 5,
}} }}
> >
{data.tags !== undefined {data.tags !== undefined && data.tags !== null
? data.tags.map((tag, index) => { ? data.tags.map((tag, index) => {
if (index >= 3) { if (index >= 3) {
return null; return null;