Bunch of fixes for MSSP feature buildout
This commit is contained in:
@@ -134,6 +134,9 @@ const AppStats = (defaultprops) => {
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
}).catch((error) => {
|
||||
console.log("Error getting workflow stats: " + error);
|
||||
return workflow
|
||||
})
|
||||
|
||||
if (response.status !== 200) {
|
||||
|
||||
@@ -428,7 +428,6 @@ const ConfigureWorkflow = (props) => {
|
||||
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)
|
||||
|
||||
@@ -449,9 +448,6 @@ const ConfigureWorkflow = (props) => {
|
||||
|
||||
newactions[foundindex].show_steps = true
|
||||
|
||||
console.log("CHANGED ACTION: ", newactions[foundindex])
|
||||
//console.log("Index: ", newactions[foundindex])
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -1321,7 +1317,6 @@ const ConfigureWorkflow = (props) => {
|
||||
}
|
||||
|
||||
if (step.type === "authenticate") {
|
||||
console.log("AUTH STEP: ", step)
|
||||
if (data.must_authenticate === true ) {
|
||||
filled = false
|
||||
} else {
|
||||
|
||||
@@ -78,7 +78,6 @@ const EditWorkflow = (props) => {
|
||||
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
||||
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
||||
|
||||
console.log("WORKFLOW: ", workflow)
|
||||
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
|
||||
|
||||
const classes = useStyles();
|
||||
@@ -235,7 +234,7 @@ const EditWorkflow = (props) => {
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<FormControl>
|
||||
<div style={{width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
||||
<div style={{borderTop: "1px solid rgba(255,255,255,0.5)", width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
||||
{/*
|
||||
<Button
|
||||
style={{}}
|
||||
@@ -315,7 +314,7 @@ const EditWorkflow = (props) => {
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Save Changes"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -565,14 +564,22 @@ const EditWorkflow = (props) => {
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<Typography variant="body2" style={{marginTop: 50, }}>
|
||||
MSSP Suborg Distribution (beta - contact support@shuffler.io)
|
||||
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||
MSSP Suborg Distribution (beta - contact support@shuffler.io for more info)
|
||||
</Typography>
|
||||
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||
You can only distribute to suborgs from a parent org.
|
||||
</Typography>
|
||||
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||
Your organization does not have any suborgs yet. Please <a href="/admin?tab=suborgs" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">make one</a>, then try again.
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||
{innerWorkflow.parentorg_workflow !== undefined && innerWorkflow.parentorg_workflow !== null && innerWorkflow.parentorg_workflow.length > 0 ? <span>This workflow is distributed from <a href={`/workflows/${innerWorkflow.parentorg_workflow}`} style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">your parent workflow</a> (you may not have access).</span> : null}
|
||||
<br />
|
||||
<br />
|
||||
You can only distribute to suborgs from a parent org.
|
||||
</Typography>
|
||||
:
|
||||
<Select
|
||||
multiple
|
||||
@@ -584,8 +591,6 @@ const EditWorkflow = (props) => {
|
||||
newvalue = newvalue.filter(value => value !== "none")
|
||||
}
|
||||
|
||||
console.log("NEWVALUE: ", newvalue)
|
||||
|
||||
if (newvalue.includes("none")) {
|
||||
newvalue = ["none"]
|
||||
} else if (newvalue.includes("all")) {
|
||||
@@ -775,18 +780,172 @@ const EditWorkflow = (props) => {
|
||||
>
|
||||
<AddIcon style={{}} />
|
||||
</Button>
|
||||
|
||||
|
||||
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||
Git Backup Repository
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides the <a href="/admin?admin_tab=organization" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">default backup repository</a> your org has chosen. All fields must be filled in for the backup to work.
|
||||
<br />
|
||||
PS: This is a beta feature, and might not work as expected. Credentials are NOT encrypted.
|
||||
</Typography>
|
||||
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
<Typography>Workflow Backup Repository</Typography>
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
multiline={true}
|
||||
rows={1}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="github/com/shuffle/workflowbackup "
|
||||
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_repo === undefined || innerWorkflow.backup_config.upload_repo === null || innerWorkflow.backup_config.upload_repo === "" ? "" : innerWorkflow.backup_config.upload_repo}
|
||||
onChange={(e) => {
|
||||
//setUploadRepo(e.target.value);
|
||||
innerWorkflow.backup_config.upload_repo = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</Grid>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
<Typography>Branch</Typography>
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
multiline={true}
|
||||
rows={1}
|
||||
placeholder="The branch to use"
|
||||
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_branch === undefined || innerWorkflow.backup_config.upload_branch === null || innerWorkflow.backup_config.upload_branch === "" ? "" : innerWorkflow.backup_config.upload_branch}
|
||||
onChange={(e) => {
|
||||
innerWorkflow.backup_config.upload_branch = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container style={{ }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
<Typography>Username</Typography>
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
multiline={true}
|
||||
rows={1}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="The username to use"
|
||||
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_username === undefined || innerWorkflow.backup_config.upload_username === null || innerWorkflow.backup_config.upload_username === "" ? "" : innerWorkflow.backup_config.upload_username}
|
||||
onChange={(e) => {
|
||||
innerWorkflow.backup_config.upload_username = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</Grid>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
<Typography>Git token/password</Typography>
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
fullWidth={true}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
multiline={true}
|
||||
rows={1}
|
||||
placeholder="The API token to use"
|
||||
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_token === undefined || innerWorkflow.backup_config.upload_token === null || innerWorkflow.backup_config.upload_token === "" ? "" : innerWorkflow.backup_config.upload_token}
|
||||
onChange={(e) => {
|
||||
innerWorkflow.backup_config.upload_token = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
type="password"
|
||||
/>
|
||||
</span>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</span>
|
||||
: null}
|
||||
|
||||
|
||||
<Tooltip color="primary" title={"Add more details"} placement="top">
|
||||
<IconButton
|
||||
style={{ color: "white", margin: "auto", textAlign: "center", width: 50, marginTop: 50, }}
|
||||
<Button
|
||||
style={{ margin: "auto", marginTop: 50, textAlign: "center", }}
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setShowMoreClicked(!showMoreClicked);
|
||||
}}
|
||||
>
|
||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||
</IconButton>
|
||||
{showMoreClicked ? "Collapse": "Expand"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import theme from "../theme.jsx";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
@@ -84,6 +84,15 @@ const Header = (props) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const topbar = localStorage.getItem("topbar_closed")
|
||||
if (topbar === "true") {
|
||||
setShowTopbar(false)
|
||||
} else {
|
||||
setShowTopbar(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
setAnchorElAvatar(null);
|
||||
@@ -1590,17 +1599,17 @@ const Header = (props) => {
|
||||
|
||||
navigate("/training")
|
||||
|
||||
//if (window.drift !== undefined) {
|
||||
// window.drift.api.startInteraction({ interactionId: 341911 })
|
||||
//} else {
|
||||
// console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
||||
//}
|
||||
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
||||
Public Training!
|
||||
</span>
|
||||
</u>
|
||||
</Typography>
|
||||
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => { setShowTopbar(false) }}>
|
||||
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => {
|
||||
setShowTopbar(false)
|
||||
|
||||
// Set storage that it's clicked
|
||||
localStorage.setItem("topbar_closed", "true")
|
||||
}}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -167,6 +167,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
listCache,
|
||||
|
||||
authGroups,
|
||||
apps,
|
||||
setEditorData,
|
||||
setcodedata,
|
||||
@@ -1123,6 +1124,65 @@ const ParsedAction = (props) => {
|
||||
return helperText
|
||||
}
|
||||
|
||||
const analyzeFields = () => {
|
||||
|
||||
if (selectedAction === undefined || selectedAction === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Only shuffle tools for now
|
||||
if (selectedAction.app_name !== "Shuffle Tools") {
|
||||
return null
|
||||
}
|
||||
|
||||
// Custom rules
|
||||
if (selectedAction.name === "set_cache_value") {
|
||||
var actionKey = ""
|
||||
var actionValue = ""
|
||||
for (let [key,keyval] in Object.entries(selectedActionParameters)) {
|
||||
const param = selectedActionParameters[key]
|
||||
if (param.name === "key") {
|
||||
actionKey = param.value
|
||||
}
|
||||
|
||||
if (param.name === "value") {
|
||||
actionValue = param.value
|
||||
}
|
||||
}
|
||||
|
||||
if (actionKey === "" || actionValue === "") {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!actionKey.includes(".#") && actionValue.includes(".#")) {
|
||||
return <span>When the key ({actionKey}) is static, but the value is a list ({actionValue}), it will overwrite the list. You may be looking for the <span onClick={() => {}} style={{cursor: "pointer", color: "#f85a3e", }}>Check Cache Contains</span> action instead.</span>
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
}
|
||||
|
||||
const suggestionInfo = () => {
|
||||
const suggestionText = analyzeFields()
|
||||
if (suggestionText === undefined || suggestionText === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (selectedAction.errors === undefined || selectedAction.errors === null || selectedAction.errors.length === 0) {
|
||||
selectedAction.errors = ["Suggestion: " + suggestionText]
|
||||
}
|
||||
|
||||
return <Paper style={{padding: 10, backgroundColor: theme.palette.surfaceColor, border: "1px solid red",}}>
|
||||
<Typography variant="body" style={{color: "white", }}>
|
||||
<b>Tip:</b> {suggestionText}
|
||||
</Typography>
|
||||
</Paper>
|
||||
}
|
||||
|
||||
// FIXME: Issue #40 - selectedActionParameters not reset
|
||||
if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) {
|
||||
@@ -1310,6 +1370,7 @@ const ParsedAction = (props) => {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
{selectedAction.template === true && selectedAction.matching_actions !== undefined && selectedAction.matching_actions !== null && selectedAction.matching_actions.length > 0 ?
|
||||
<div>
|
||||
<Typography variant="body1">
|
||||
@@ -1441,6 +1502,8 @@ const ParsedAction = (props) => {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{suggestionInfo()}
|
||||
|
||||
{selectedActionParameters.map((data, count) => {
|
||||
if (data.variant === "") {
|
||||
data.variant = "STATIC_VALUE";
|
||||
@@ -3606,6 +3669,7 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
labelId="select-app-auth"
|
||||
value={
|
||||
selectedAction.authentication_id === "authgroups" ? "authgroups" :
|
||||
Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0
|
||||
? "No selection"
|
||||
: selectedAction.selectedAuthentication
|
||||
@@ -3617,6 +3681,8 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
console.log("AUTH CHANGE: ", e.target.value)
|
||||
|
||||
if (e.target.value === "No selection") {
|
||||
selectedAction.selectedAuthentication = {};
|
||||
selectedAction.authentication_id = "";
|
||||
@@ -3628,13 +3694,36 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
}
|
||||
setSelectedAction(selectedAction);
|
||||
setUpdate(Math.random());
|
||||
setUpdate(Math.random())
|
||||
|
||||
} else if (e.target.value === "authgroups") {
|
||||
if (authGroups !== undefined && authGroups !== null && authGroups.length === 0) {
|
||||
toast("No auth groups created. Opening window to create one")
|
||||
|
||||
setTimeout(() => {
|
||||
window.open("/admin?tab=app_auth", "_blank")
|
||||
}, 2500)
|
||||
} else {
|
||||
selectedAction.selectedAuthentication = {};
|
||||
selectedAction.authentication_id = "authgroups"
|
||||
|
||||
for (let [key,keyval] in Object.entries(selectedAction.parameters)) {
|
||||
//console.log(selectedAction.parameters[key])
|
||||
if (selectedAction.parameters[key].configuration) {
|
||||
selectedAction.parameters[key].value = "authgroup controlled"
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedAction(selectedAction)
|
||||
setUpdate(Math.random())
|
||||
}
|
||||
} else {
|
||||
selectedAction.selectedAuthentication = e.target.value;
|
||||
selectedAction.authentication_id = e.target.value.id;
|
||||
setSelectedAction(selectedAction);
|
||||
setUpdate(Math.random());
|
||||
}
|
||||
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
@@ -3653,6 +3742,7 @@ const ParsedAction = (props) => {
|
||||
>
|
||||
<em>No selection</em>
|
||||
</MenuItem>
|
||||
|
||||
{selectedAction.authentication.map((data) => {
|
||||
if (data.last_modified === true) {
|
||||
//console.log("LAST MODIFIED: ", data.label)
|
||||
@@ -3689,6 +3779,20 @@ const ParsedAction = (props) => {
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
<Divider style={{marginTop: 10, marginBottom: 10, }}/>
|
||||
|
||||
<MenuItem
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
}}
|
||||
value="authgroups"
|
||||
>
|
||||
<em>Auth Groups</em>
|
||||
</MenuItem>
|
||||
|
||||
</Select>
|
||||
|
||||
{/*
|
||||
|
||||
@@ -627,8 +627,8 @@ const CodeEditor = (props) => {
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i+1,
|
||||
endCol: endCh+1,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: correctVariable ? "good-marker" : "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user