diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 51690770..a7e1e7bf 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1212,7 +1212,7 @@ class AppBase: return results # Downloads all files from a namespace - # Currently only working on local version of Shuffle + # Currently only working on local version of Shuffle (2023) def get_file_category_ids(self, category): org_id = self.full_execution["workflow"]["execution_org"]["id"] @@ -1914,7 +1914,6 @@ class AppBase: c_parentheses = parse_nested_param(parse_string, 0)[0] match_string = re.escape(c_parentheses) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) - print("[DEBUG] In ELSE: %s" % custom_casting) # check if a wrapper was found if len(custom_casting) != 0: inner_result = parse_type(c_parentheses, custom_casting[0]) @@ -2093,7 +2092,6 @@ class AppBase: except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - #print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if isinstance(firstitem, str): if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end": firstitem = len(basejson)-1 diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 98f8f418..ccfc643b 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared go 1.19 @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.5.65 + github.com/shuffle/shuffle-shared v0.5.78 golang.org/x/crypto v0.16.0 google.golang.org/api v0.125.0 google.golang.org/grpc v1.55.0 @@ -52,7 +52,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/frikky/schemaless v0.0.5 // indirect + github.com/frikky/schemaless v0.0.6 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 3bdc7d06..bdd5b7b7 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -176,8 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= -github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY= -github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= +github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg= +github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= @@ -455,6 +455,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE= +github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b2e901d5..0eca60ba 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4953,6 +4953,7 @@ func initHandlers() { // PS: For cloud, this has to use cloud storage. // https://developer.box.com/reference/get-files-id-content/ // 1. Creating the "get file" option. Make it possible to run this in the frontend. + r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index c1e71f2f..a9d72774 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -552,7 +552,9 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { ctx := context.Background() workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { - log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err) + if len(actionResult.ExecutionId) > 0 { + log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err) + } resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) return diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 383553fd..fbdf7fa0 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -40,12 +40,12 @@ cytoscape.use(edgehandles); export const findSpecificApp = (framework, inputcategory) => { // Get the frameworkinfo for the org and fill in if (framework === undefined || framework === null) { - console.log("findSpecificApp: framework is null") + //console.log("findSpecificApp: framework is null") return null } if (inputcategory === undefined || inputcategory === null) { - console.log("findSpecificApp: category is null") + //console.log("findSpecificApp: category is null") return null } @@ -118,8 +118,8 @@ export const findSpecificApp = (framework, inputcategory) => { } return { - name: "EDR :default", - large_image: parsedDatatypeImages["EDR & AV"], + name: "IAM :default", + large_image: parsedDatatypeImages["IAM"], count: 0, description: "", id: "", @@ -654,7 +654,7 @@ const AppFramework = (props) => { const handleLoadNextSuggestion = (frameworkData) => { - console.log("Should check for next apps to load from App suggestion model") + //console.log("Should check for next apps to load from App suggestion model") //fetch(globalUrl + "/api/v1/workflows/usecases", { //credentials: "include", //cors: "no-cors", @@ -720,7 +720,7 @@ const AppFramework = (props) => { } const showRecommendations = (changed, frameworkData) => { - console.log("Inside recommendation loader") + //console.log("Inside recommendation loader") setChangedApp(changed) handleLoadNextSuggestion(frameworkData) @@ -761,7 +761,7 @@ const AppFramework = (props) => { //console.log("APptype, changed, framework: ", apptype.toLowerCase(), alternativeChanged.toLowerCase(), changed.toLowerCase(), frameworkData) if (changed.toLowerCase() === apptype.toLowerCase() || changed.toLowerCase().includes(apptype.toLowerCase()) || alternativeChanged.toLowerCase() === apptype.toLowerCase() || alternativeChanged.toLowerCase().includes(apptype.toLowerCase())) { potential = true - console.log("Potential: !", apptype) + //console.log("Potential: !", apptype) if (frameworkData[apptype] !== undefined && frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) { usecase.items[itemtype].app = frameworkData[apptype] @@ -819,10 +819,10 @@ const AppFramework = (props) => { continue } } else { - console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)") + //console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)") } - console.log("Usecase: ", usecase) + //console.log("Usecase: ", usecase) if (matches.length === usecase.items.length) { usecase.color = "#c51152" usecase.type = usecaseTypes[key].name @@ -833,8 +833,8 @@ const AppFramework = (props) => { } // FIXME: Check if a usecase has already been handled - console.log("") - console.log("GOT USECASES: ", showusecases) + //console.log("") + //console.log("GOT USECASES: ", showusecases) // FIXME: Just showing one usecase at a time for now if (showusecases.length > 0) { @@ -894,38 +894,37 @@ const AppFramework = (props) => { parsedUsecase.process.push(edge.data) } - fetch(globalUrl + "/api/v1/workflows/usecases", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(parsedUsecase), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for framework!"); - } + fetch(globalUrl + "/api/v1/workflows/usecases", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(parsedUsecase), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { - toast("Failed updating: " + responseJson.reason) - } else { - toast("Failed to update framework for your org.") - } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed updating: " + responseJson.reason) } else { - toast("Updated usecase.") + toast("Failed to update framework for your org.") } - }) - .catch((error) => { - toast(error.toString()); - //setFrameworkLoaded(true) - }) - } + } else { + toast("Updated usecase.") + } + }) + .catch((error) => { + toast(error.toString()); + }) + } const activateApp = (appid) => { fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", { @@ -957,45 +956,54 @@ const AppFramework = (props) => { } const setFrameworkItem = (data) => { - console.log("Setting framework item: ", data, isCloud) if (!isCloud) { activateApp(data.id) } - fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(data), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for framework!"); - } + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { - toast("Failed updating: " + responseJson.reason) - } else { - toast("Failed to update framework for your org.") + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed getting appframework: " + responseJson.reason) + } else { + toast("Failed to update framework for your org.") - } - } + } + } else { + if (data.id === "remove") { + frameworkData[data.type] = {} + frameworkData[data.type.toLowerCase()] = {} + } else { + frameworkData[data.type] = data + frameworkData[data.type.toLowerCase()] = data + } - //setFrameworkLoaded(true) - //setFrameworkData(responseJson) - }) - .catch((error) => { - toast(error.toString()); - //setFrameworkLoaded(true) - }) - } + setDiscoveryData({}) + if (setFrameworkData !== undefined) { + setFrameworkData(frameworkData) + } + } + }) + .catch((error) => { + toast(error.toString()); + //setFrameworkLoaded(true) + }) + } useEffect(() => { if (!window.location.pathname.includes("usecases")) { @@ -1058,7 +1066,6 @@ const AppFramework = (props) => { frameworkData[keys[key]] = submitValue } - console.log("Frameworkdata: ", frameworkData) setFrameworkData(frameworkData) if (discoveryData.large_image !== undefined && discoveryData.large_image !== null && discoveryData.large_image.includes("storage.googleapis.com")) { @@ -1076,8 +1083,6 @@ const AppFramework = (props) => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - - const imgSize = 50; var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData @@ -1263,7 +1268,7 @@ const AppFramework = (props) => { var parsedStyle2 = { "ghost": "yes", - "border-width": "7px", + "border-width": "7px", } /* @@ -1399,7 +1404,6 @@ const AppFramework = (props) => { const onNodeSelect = (event) => { var data = event.target.data(); - console.log("Node: ", data) if (data.id === "SHUFFLE") { event.target.unselect() return @@ -1465,6 +1469,15 @@ const AppFramework = (props) => { const foundMiddleImage = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : '/images/Shuffle_logo.png' + const siemcheck = parsedFrameworkData.SIEM.large_image === undefined || (parsedFrameworkData.SIEM.name !== undefined && parsedFrameworkData.SIEM.name !== null && parsedFrameworkData.SIEM.name.includes(":default")) + const iamcheck = parsedFrameworkData.IAM.large_image === undefined || (parsedFrameworkData.IAM.name !== undefined && parsedFrameworkData.IAM.name !== null && parsedFrameworkData.IAM.name.includes(":default")) + const casescheck = parsedFrameworkData.Cases.large_image === undefined || (parsedFrameworkData.Cases.name !== undefined && parsedFrameworkData.Cases.name !== null && parsedFrameworkData.Cases.name.includes(":default")) + const assetscheck = parsedFrameworkData.Assets.large_image === undefined || (parsedFrameworkData.Assets.name !== undefined && parsedFrameworkData.Assets.name !== null && parsedFrameworkData.Assets.name.includes(":default")) + const intelcheck = parsedFrameworkData.Intel.large_image === undefined || (parsedFrameworkData.Intel.name !== undefined && parsedFrameworkData.Intel.name !== null && parsedFrameworkData.Intel.name.includes(":default")) + const commscheck = parsedFrameworkData.Comms.large_image === undefined || (parsedFrameworkData.Comms.name !== undefined && parsedFrameworkData.Comms.name !== null && parsedFrameworkData.Comms.name.includes(":default")) + const edrcheck = parsedFrameworkData["EDR & AV"].large_image === undefined || (parsedFrameworkData["EDR & AV"].name !== undefined && parsedFrameworkData["EDR & AV"].name !== null && parsedFrameworkData["EDR & AV"].name.includes(":default")) + const networkcheck = parsedFrameworkData.Network.large_image === undefined || (parsedFrameworkData.Network.name !== undefined && parsedFrameworkData.Network.name !== null && parsedFrameworkData.Network.name.includes(":default")) + const fontSize = `${12*scale}px` const defaultSize = `${85*scale}px` @@ -1484,11 +1497,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name, description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description, app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id, - text_margin_y: parsedFrameworkData.Cases.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Cases.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Cases.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize, + text_margin_y: casescheck ? textMarginDefault : textMarginImage, + margin_x: casescheck ? `${32*scale}px` : "0px", + margin_y: casescheck ? `${19*scale}px` : `0px`, + width: casescheck ? iconSize : defaultSize, + height: casescheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image, label: securityFramework[0].text.toUpperCase(), @@ -1501,6 +1514,7 @@ const AppFramework = (props) => { } }, { + group: "nodes", data: { font_size: fontSize, @@ -1512,11 +1526,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name, description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description, app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id, - text_margin_y: parsedFrameworkData.IAM.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.IAM.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.IAM.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize, + text_margin_y: iamcheck ? textMarginDefault : textMarginImage, + margin_x: iamcheck ? `${32*scale}px` : "0px", + margin_y: iamcheck ? `${19*scale}px` : `0px`, + width: iamcheck ? iconSize : defaultSize, + height: iamcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image, label: securityFramework[3].text.toUpperCase(), @@ -1540,11 +1554,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name, description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description, app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id, - text_margin_y: parsedFrameworkData.Assets.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Assets.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Assets.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize, + text_margin_y: assetscheck ? textMarginDefault : textMarginImage, + margin_x: assetscheck ? `${32*scale}px` : "0px", + margin_y: assetscheck ? `${19*scale}px` : `0px`, + width: assetscheck ? iconSize : defaultSize, + height: assetscheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image, label: securityFramework[2].text.toUpperCase(), @@ -1568,11 +1582,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name, description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description, app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id, - text_margin_y: parsedFrameworkData.Intel.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Intel.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Intel.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize, + text_margin_y: intelcheck ? textMarginDefault : textMarginImage, + margin_x: intelcheck ? `${32*scale}px` : "0px", + margin_y: intelcheck ? `${19*scale}px` : `0px`, + width: intelcheck ? iconSize : defaultSize, + height: intelcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image, label: securityFramework[4].text.toUpperCase(), @@ -1596,11 +1610,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name, description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description, app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id, - text_margin_y: parsedFrameworkData.Comms.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Comms.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Comms.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize, + text_margin_y: commscheck ? textMarginDefault : textMarginImage, + margin_x: commscheck ? `${32*scale}px` : "0px", + margin_y: commscheck ? `${19*scale}px` : `0px`, + width: commscheck ? iconSize : defaultSize, + height: commscheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image, label: securityFramework[5].text.toUpperCase(), @@ -1624,11 +1638,11 @@ const AppFramework = (props) => { name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name, description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description, app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id, - text_margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize, + text_margin_y: edrcheck ? textMarginDefault : textMarginImage, + margin_x: edrcheck ? `${32*scale}px` : "0px", + margin_y: edrcheck ? `${19*scale}px` : `0px`, + width: edrcheck ? iconSize : defaultSize, + height: edrcheck ? iconSize : defaultSize, large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image, label: securityFramework[7].text.toUpperCase(), @@ -1652,11 +1666,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name, description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description, app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id, - text_margin_y: parsedFrameworkData.Network.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.Network.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.Network.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize, + text_margin_y: networkcheck ? textMarginDefault : textMarginImage, + margin_x: networkcheck ? `${32*scale}px` : "0px", + margin_y: networkcheck ? `${19*scale}px` : `0px`, + width: networkcheck ? iconSize : defaultSize, + height: networkcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image, label: securityFramework[6].text.toUpperCase(), id: securityFramework[6].text.toUpperCase(), @@ -1679,11 +1693,11 @@ const AppFramework = (props) => { name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name, description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description, app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id, - text_margin_y: parsedFrameworkData.SIEM.large_image === undefined ? textMarginDefault : textMarginImage, - margin_x: parsedFrameworkData.SIEM.large_image === undefined ? `${32*scale}px` : "0px", - margin_y: parsedFrameworkData.SIEM.large_image === undefined ? `${19*scale}px` : `0px`, - width: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, - height: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize, + text_margin_y: siemcheck ? textMarginDefault : textMarginImage, + margin_x: siemcheck ? `${32*scale}px` : "0px", + margin_y: siemcheck ? `${19*scale}px` : `0px`, + width: siemcheck ? iconSize : defaultSize, + height: siemcheck ? iconSize : defaultSize, large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image, label: securityFramework[1].text.toUpperCase(), id: securityFramework[1].text.toUpperCase(), @@ -1988,6 +2002,7 @@ const AppFramework = (props) => { const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color return (
+ {/*
{ inputSearch={changedApp} apps={apps} /> -
- {injectedApps.map((apps, appindex) => { +
+ */} + + {/*injectedApps.map((apps, appindex) => { var categoryTop = 100 var categoryLeft = 100 @@ -2028,8 +2045,11 @@ const AppFramework = (props) => { return null } + const scale = 0.9 + const offsetTop = -70 + const offsetLeft = 0 return ( -
+
{apps.map((app, appIndex) => { return ( { } if (setFrameworkData !== undefined) { - console.log("Setting frameworkdata") // Find discoveryData.id var keys = [] for (const [key, value] of Object.entries(frameworkData)) { @@ -2088,7 +2107,7 @@ const AppFramework = (props) => { })}
) - })} + })*/} {showOptions === false ? null :
diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx index 553b7e41..262843ad 100644 --- a/frontend/src/components/AppSearchButtons.jsx +++ b/frontend/src/components/AppSearchButtons.jsx @@ -37,7 +37,7 @@ import { } from '@mui/material'; const AppSearchButtons = (props) => { - const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props + const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props const ref = useRef() let navigate = useNavigate(); @@ -48,8 +48,6 @@ const AppSearchButtons = (props) => { useEffect(() => { if (newSelectedApp !== undefined && setMissing != undefined) { - console.log("AppSearchButtons: setMissing is defined!") - const submitAppFramework = { "description": newSelectedApp.description, "id": newSelectedApp.objectID, @@ -60,11 +58,17 @@ const AppSearchButtons = (props) => { setFrameworkItem(submitAppFramework) setMissing(newSelectedApp) + + if (getAppFramework !== undefined) { + setTimeout(() => { + getAppFramework() + }, 1000) + } } }, [newSelectedApp]) if (appType === undefined) { - console.log("Apptype is required in AppSearchButtons") + //console.log("Apptype is required in AppSearchButtons") return null; } diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 81e9fd3d..e3938c65 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -77,10 +77,7 @@ const ConfigureWorkflow = (props) => { const [checkStarted, setCheckStarted] = React.useState(false); useEffect(() => { - console.log("Required actions: ", requiredActions) if (requiredActions.length === 0) { - console.log("No more actions? Set parent to done?") - if (setConfigurationFinished !== undefined) { setConfigurationFinished(true) } @@ -705,9 +702,9 @@ const ConfigureWorkflow = (props) => { }) .then((responseJson) => { if (!responseJson.success) { - toast("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app authentication: " + responseJson.reason); } else { - toast("App auth set for app " + app.name.replace("_", " ")); + toast("App authentication set for app " + app.name.replace("_", " ")); setFinalized(true) setOpened(false) } @@ -731,6 +728,7 @@ const ConfigureWorkflow = (props) => { parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); + console.log("AUTH Action: ", action) return ( {
{ setOpened(!opened); + + // Scroll to it + const element = document.getElementById("app-config"); + if (element) { + // Scroll down 100px + setTimeout(() => { + element.scrollIntoView({ + behavior: "smooth", + top: 100, + }) + + //element.scrollIntoView({ + // behavior: "smooth", + // block: "center", + // inline: "center" + //}); + }, 250) + } + }} >
@@ -1318,7 +1335,7 @@ const ConfigureWorkflow = (props) => { : null} - + {requiredActions.map((data, index) => { // AppWrapper = Default in a workflow, only shows with steps diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 05e57184..081b6f92 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -146,8 +146,6 @@ const EditWorkflow = (props) => { const newWorkflow = isEditing === true ? false : true const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) - console.log("PRIO: ", priority) - var upload = ""; var total_count = 0 @@ -485,7 +483,7 @@ const EditWorkflow = (props) => { - + + + + + : null + const deleteFile = (file) => { fetch(globalUrl + "/api/v1/files/" + file.id, { method: "DELETE", @@ -140,7 +384,7 @@ const Files = (props) => { }) .then((responseJson) => { if (responseJson.success) { - toast("Successfully deleted file " + file.name); + toast("Successfully deleted file") } else if ( responseJson.reason !== undefined && responseJson.reason !== null @@ -254,7 +498,7 @@ const Files = (props) => { }); }; - const handleCreateFile = (filename, file) => { + const handleCreateFile = (filename, file) => { var data = { filename: filename, org_id: selectedOrganization.id, @@ -355,7 +599,7 @@ const Files = (props) => { } setTimeout(() => { - getFiles(); + getFiles() }, 2500); }; @@ -378,7 +622,21 @@ const Files = (props) => { }} onDrop={uploadFile} > -
+
+ + + setLoadFileModalOpen(true)} + > + + + + + {fileDownloadModal} +

Files

@@ -393,6 +651,9 @@ const Files = (props) => {
+ + + + + {fileNamespaces !== undefined && fileNamespaces !== null && fileNamespaces.length > 1 ? ( @@ -442,8 +705,13 @@ const Files = (props) => { }} value={selectedNamespace} onChange={(event) => { - console.log("CHANGE NAMESPACE: ", event.target); setSelectedNamespace(event.target.value); + + if (event.target.value === "all" || event.target.value === "default") { + getFiles() + } else { + getFiles(event.target.value) + } }} > {fileNamespaces.map((data, index) => { @@ -453,7 +721,7 @@ const Files = (props) => { value={data} style={{ color: "white" }} > - {data} + {data.replaceAll("_", " ")} ); })} @@ -487,6 +755,8 @@ const Files = (props) => { } + + {renderTextBox && { handleKeyDown(event); @@ -504,7 +774,7 @@ const Files = (props) => { autoFocus />}
- { /> @@ -615,7 +885,7 @@ const Files = (props) => { }} /> - ) : ( + : ( { }; - const returnHelperText = (name, value) => { - if (name === undefined || value === undefined) { - return "" - } - - var helperText = "" - //console.log("DATA: ", name, value) - if (name.includes("url")) { - if (value.includes("localhost") || value.includes("127.0.0.1")) { - helperText = "Can't use localhost. Please change to an external IP or hostname." - } - } - - return helperText + const returnHelperText = (name, value) => { + if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") { + //console.log("RETURNING EMPTY") + return "" } + var helperText = "" + //console.log("DATA: ", name, value) + if (name.includes("url")) { + if (value.includes("localhost") || value.includes("127.0.0.1")) { + helperText = "Can't use localhost. Please change to your external IP." + } + } + + // Helpertext for openapi fields + //if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) { + // helperText = + //} + + return helperText + } + //console.log("AUTH: ", authenticationType) if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") { /* @@ -1297,17 +1297,13 @@ const ParsedAction = (props) => { } var placeholder = "Value"; - if ( - data.example !== undefined && - data.example !== null && - data.example.length > 0 - ) { + if (data.example !== undefined && data.example !== null && data.example.length > 0) { + placeholder = data.example; if (data.name === "url" && data.value !== undefined && data.value !== null && data.value.length === 0) { data.value = data.example; } - // In case of data.example if (data.value === undefined || data.value === null) { data.value = "" @@ -1642,7 +1638,6 @@ const ParsedAction = (props) => { ), }} multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} - helperText={returnHelperText(data.name, data.value)} onClick={() => { /* setExpansionModalOpen(false); @@ -1680,18 +1675,7 @@ const ParsedAction = (props) => { //changeActionParameterCodemirror(event, count, data) changeActionParameter(event, count, data); }} - helperText={baseHelperText.length > 0 ? baseHelperText : - selectedApp.generated && - selectedApp.activated && - data.name === "body" ? ( - - {openApiHelperText} - - ) : data.name.startsWith("${") && data.name.endsWith("}") ? - null : null - } + helperText={returnHelperText(data.name, data.value)} onBlur={(event) => { baseHelperText = calculateHelpertext(event.target.value) if (setLastSaved !== undefined) { diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 63c06515..bd6e5fa8 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -67,9 +67,15 @@ const Priorities = (props) => { }) } - const dismissNotification = (alert_id) => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { + const dismissNotification = (alert_id, disabled) => { + var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread` + if (disabled === true) { + notificationurl += "?disabled=true" + } else if (disabled === false) { + notificationurl += "?disabled=false" + } + + fetch(notificationurl , { credentials: "include", method: "GET", headers: { @@ -85,12 +91,50 @@ const Priorities = (props) => { }) .then(function (responseJson) { if (responseJson.success === true) { - const newNotifications = notifications.filter( - (data) => data.id !== alert_id - ); - console.log("NEW NOTIFICATIONS: ", newNotifications); + // Mark current one as read + var newNotifications = notifications.map((notification) => { + if (notification.id === alert_id) { + notification.read = true + } - if (setNotifications !== undefined) { + return notification + }) + + + if (disabled === true) { + toast("Notification disabled, and will not be shown again.") + + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = true + } + + return notification + }) + + console.log("NEW NOTIFICATIONS: ", newNotifications); + } else if (disabled === false) { + toast("Notification re-enabled successfully") + + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = false + } + + return notification + }) + + } else { + toast("Notification dismissed successfully") + } + + //const newNotifications = notifications.filter( + // (data) => data.id !== alert_id + //) + + //console.log("NEW NOTIFICATIONS: ", newNotifications); + + if (setNotifications !== undefined && newNotifications !== undefined) { setNotifications(newNotifications) } } else { @@ -174,6 +218,14 @@ const Priorities = (props) => { style={{marginRight: 15, height: 25, }} /> : null} + {data.ignored === true ? + + : null} {data.read === false ? { Dismiss ) : null} + + + diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 13c278d3..60e4bce2 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -32,7 +32,6 @@ const Priority = (props) => { let newdescription = priority.description const descsplit = priority.description.split("&") if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) { - console.log("descsplit: ", descsplit) if (descsplit[1] === "") { const item = findSpecificApp(appFramework, descsplit[0]) diff --git a/frontend/src/components/RenderCytoscape.jsx b/frontend/src/components/RenderCytoscape.jsx index c112b448..7348fdb7 100644 --- a/frontend/src/components/RenderCytoscape.jsx +++ b/frontend/src/components/RenderCytoscape.jsx @@ -5,13 +5,13 @@ import cystyle from "../defaultCytoscapeStyle.jsx"; const surfaceColor = "#27292D"; const CytoscapeWrapper = (props) => { - const { globalUrl, inworkflow } = props; + const { globalUrl, inworkflow, height, width } = props; const [elements, setElements] = useState([]); const [workflow, setWorkflow] = useState(inworkflow); const [cy, setCy] = React.useState(); - const bodyWidth = 200; - const bodyHeight = 150; + const bodyWidth = height === undefined ? 1000 : width + const bodyHeight = width === undefined ? 1000 : height const setupGraph = () => { const actions = workflow.actions.map((action) => { @@ -76,29 +76,6 @@ const CytoscapeWrapper = (props) => { }; // This is an attempt at prettier edges. The numbers are weird to work with. - /* - //http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html - const sourcenode = actions.find(node => node.data._id === branch.source_id) - const destinationnode = actions.find(node => node.data._id === branch.destination_id) - if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) { - //node.data._id = action["id"] - console.log("SOURCE: ", sourcenode.position) - console.log("DESTINATIONNODE: ", destinationnode.position) - - var opposite = true - if (sourcenode.position.x > destinationnode.position.x) { - opposite = false - } else { - opposite = true - } - - edge.style = { - 'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"], - 'control-point-weight': ['0.3', '0.7'], - } - } - */ - return edge; }); @@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => { elements={elements} minZoom={0.35} maxZoom={2.0} + zoom={1.0} style={{ - width: bodyWidth - 15, - height: bodyHeight - 5, + width: bodyWidth, + height: bodyHeight, backgroundColor: surfaceColor, }} stylesheet={cystyle} diff --git a/frontend/src/components/RuntimeDebugger.jsx b/frontend/src/components/RuntimeDebugger.jsx index 01bb345c..416b0d76 100644 --- a/frontend/src/components/RuntimeDebugger.jsx +++ b/frontend/src/components/RuntimeDebugger.jsx @@ -75,9 +75,15 @@ const RuntimeDebugger = (props) => { setTotalCount(0) + var starttime = "" + var endtime = "" var count = 0 - var starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString() - var endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString() + try { + starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString() + endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString() + } catch (e) { + toast("Invalid date format", { type: "error" }) + } var maxworkflows = 5 @@ -88,6 +94,11 @@ const RuntimeDebugger = (props) => { } const workflowId = workflows[key].id + + if (workflowId === undefined || workflowId === null || workflowId === "") { + continue + } + // Fetch the data for the workflow var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count` if (starttime !== "") { @@ -133,7 +144,6 @@ const RuntimeDebugger = (props) => { } setTimeout(() => { - console.log("Setting total count: ", count) setTotalCount(count) }, maxworkflows*300) } @@ -578,7 +588,7 @@ const RuntimeDebugger = (props) => { onClick={() => { window.open(`${globalUrl}/api/v1/workflows/search/${params.row.id}`, "_blank") }} - disabled={!userdata.support} + disabled={userdata.region_url !== "https://shuffler.io"} > diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 15aec446..3b6e02a2 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system'; import data from '../frameworkStyle.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; @@ -84,37 +83,6 @@ const pythonFilters = [ {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, ] -/* -const shuffleTheme = createTheme({ - theme: 'dark', - settings: { - background: "rgba(40,40,40, 1)", - foreground: '#75baff', - caret: '#5d00ff', - selection: '#036dd626', - selectionMatch: '#036dd626', - lineHighlight: '#8a91991a', - gutterForeground: '#8a919966', - }, - styles: [ - { tag: t.comment, color: '#787b8099' }, - { tag: t.variableName, color: '#0080ff' }, - { tag: [t.string, t.special(t.brace)], color: '#5c6166' }, - { tag: t.number, color: '#5c6166' }, - { tag: t.bool, color: '#5c6166' }, - { tag: t.null, color: '#5c6166' }, - { tag: t.keyword, color: '#5c6166' }, - { tag: t.operator, color: '#5c6166' }, - { tag: t.className, color: '#5c6166' }, - { tag: t.definition(t.typeName), color: '#5c6166' }, - { tag: t.typeName, color: '#5c6166' }, - { tag: t.angleBracket, color: '#5c6166' }, - { tag: t.tagName, color: '#5c6166' }, - { tag: t.attributeName, color: '#5c6166' }, - ], -}); -*/ - const CodeEditor = (props) => { const { globalUrl, diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 6d3413dc..6df11b9b 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -5,6 +5,7 @@ import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import AppSearchButtons from "../components/AppSearchButtons.jsx"; import { isMobile } from "react-device-detect"; +import RenderCytoscape from "../components/RenderCytoscape.jsx"; import { Button, Typography, @@ -29,7 +30,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; const WorkflowTemplatePopup = (props) => { - const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage } = props; + const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage, getAppFramework, } = props; const [isActive, setIsActive] = useState(false); const [isHovered, setIsHovered] = useState(false); @@ -39,17 +40,41 @@ const WorkflowTemplatePopup = (props) => { const [workflow, setWorkflow] = useState({}); const [showLoginButton, setShowLoginButton] = useState(false); const [appAuthentication, setAppAuthentication] = React.useState(undefined); - const [missingSource, setMissingSource] = React.useState(undefined) const [missingDestination, setMissingDestination] = React.useState(undefined); - const [configurationFinished, setConfigurationFinished] = React.useState(false); + const [appSetupDone, setAppSetupDone] = React.useState(false) - useEffect(() => { - }, [missingSource, missingDestination]) - - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const [requestSent, setRequestSent] = React.useState(false) + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); + useEffect(() => { + if (modalOpen !== true) { + return + } + + if (!srcapp.includes(":default") && !dstapp.includes(":default")) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + + getGeneratedWorkflow() + } + + if (missingSource !== undefined && missingDestination !== undefined) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + } + + if (getAppFramework !== undefined) { + setTimeout(() => { + getAppFramework() + }, 500) + } + }, [modalOpen, missingSource, missingDestination]) + const imagestyleWrapper = { height: 40, @@ -272,6 +297,9 @@ const WorkflowTemplatePopup = (props) => { // middle:[] // name: "Email analysis" // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name + if (requestSent === true) { + return + } if (srcapp.includes(":default") || dstapp.includes(":default")) { @@ -309,9 +337,8 @@ const WorkflowTemplatePopup = (props) => { }, } + setRequestSent(true) const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge` - //const url = `https://shuffler.io/api/v1/workflows/merge` - fetch(url, { method: "POST", headers: { @@ -324,6 +351,7 @@ const WorkflowTemplatePopup = (props) => { .then((response) => { if (response.status !== 200) { //console.log("Status not 200 for framework!"); + setRequestSent(false) } setWorkflowLoading(false) @@ -341,6 +369,7 @@ const WorkflowTemplatePopup = (props) => { if (responseJson.success === false) { //console.log("Error in workflow template: ", responseJson.error); + setRequestSent(false) const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance." if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") { @@ -366,10 +395,21 @@ const WorkflowTemplatePopup = (props) => { }) .catch((error) => { console.log("err in framework: ", error.toString()); + setRequestSent(false) setWorkflowLoading(false) }) } + if (modalOpen === true && !srcapp.includes(":default") && !dstapp.includes(":default")) { + if (appSetupDone === false && setAppSetupDone !== undefined) { + setAppSetupDone(true) + } + + if (workflow.id === undefined && workflowLoading === false && errorMessage === "") { + getGeneratedWorkflow() + } + } + const isFinished = () => { // Look for configuration fields being done in the current modal // 1. Start by finding the modal @@ -388,6 +428,13 @@ const WorkflowTemplatePopup = (props) => { } const ModalView = () => { + if (modalOpen === false) { + return null + } + + const divHeight = 500 + const divWidth = 500 + return ( {
} - {(missingSource !== undefined || missingDestination !== undefined) ? + {(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ? - {"Find relevevant Apps for this Usecase"} + {"Find relevant Apps for this Usecase"} : null} @@ -491,6 +538,8 @@ const WorkflowTemplatePopup = (props) => { AppImage={missingSource.image} setMissing={setMissingSource} + + getAppFramework={getAppFramework} />
: null} @@ -505,6 +554,8 @@ const WorkflowTemplatePopup = (props) => { AppImage={missingDestination.image} setMissing={setMissingDestination} + + getAppFramework={getAppFramework} />
: null} @@ -521,12 +572,29 @@ const WorkflowTemplatePopup = (props) => { setConfigurationFinished={setConfigurationFinished} /> + + {/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ? +
+ +
+ : null*/} + {errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ? - - + + { + // Open in new tab + window.open("/workflows/" + workflow.id, "_blank") + }} + > {/* */} - - Workflow generated! + + Workflow Successfully Generated! @@ -648,6 +716,7 @@ const WorkflowTemplatePopup = (props) => { : ""}
+
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index f653bd2c..984ebb30 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles"; import { useNavigate, Link } from "react-router-dom"; import countries from "../components/Countries.jsx"; -import CodeEditor from "../components/ShuffleCodeEditor.jsx"; -import getLocalCodeData from "../components/ShuffleCodeEditor.jsx"; import CacheView from "../components/CacheView.jsx"; import { @@ -1867,16 +1865,24 @@ If you're interested, please let me know a time that works for you, or set up a {selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
- Only the name of the auth can be modified for Oauth2. Please remake the authentication to change the fields like Client ID, Secret, Scopes etc. + Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc.
- : - selectedAuthentication.fields.map((data, index) => { + : null } + + {selectedAuthentication.fields.map((data, index) => { var fieldname = data.key.replaceAll("_", " ") if (fieldname.endsWith(" basic")) { fieldname = fieldname.substring(0, fieldname.length - 6) } + if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") { + if (selectedAuthentication.fields[index].key !== "url") { + return null + } + } + + //console.log("DATA: ", data, selectedAuthentication) return (
@@ -3415,24 +3421,26 @@ If you're interested, please let me know a time that works for you, or set up a // using request id or trace id - @@ -4406,7 +4415,7 @@ If you're interested, please let me know a time that works for you, or set up a /> setDefaultEnvironment(environment)} color="primary" > diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3f397674..a54d2f16 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles"; import CytoscapeComponent from "react-cytoscapejs"; import Draggable from "react-draggable"; import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; -import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; @@ -13366,6 +13366,67 @@ const AngularWorkflow = (defaultprops) => {

{workflow.name}

+ + {workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id ? null : + + Warning: Change { + toast("Changing to correct organization. Please wait a few seconds.") + + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify({"org_id": workflow.org_id}), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + console.log("In here?") + if (responseJson.success === true) { + if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload(); + }, 2000); + + toast("Successfully changed active organization - refreshing!"); + } else { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + toast(responseJson.reason); + } else { + toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + } + } + }) + .catch((error) => { + console.log("error changing: ", error); + //removeCookie("session_token", {path: "/"}) + }) + + + + }} + >Active Organization to edit this Workflow. + + }
{parentWorkflows.slice(0,5).map((wf, index) => { @@ -13744,9 +13805,64 @@ const AngularWorkflow = (defaultprops) => {
: null + + const RightsideBar = () => { const [hovered, setHovered] = useState(false) + useEffect(() => { + const handleKeyDown = (event) => { + if ((event.metaKey || event.ctrlKey) && event.key === '/') { + event.preventDefault(); // Prevent default browser behavior (like opening search bar) + if (!workflow.public && !executionRequestStarted) { + executeWorkflow(executionText, workflow.start, lastSaved); + } + } + if ((event.ctrlKey || event.metaKey) && event.key === "'") { + // Check if Ctrl (Windows/Linux) or Command (Mac) key is pressed along with '/' + if (!workflow.public && !executionModalOpen) { + setExecutionModalOpen(true); + getWorkflowExecution(props.match.params.key, ""); + } else if (!workflow.public && executionModalOpen) { + setExecutionModalOpen(false); + } + } + + if ((event.ctrlKey || event.metaKey) && event.key === "]") { + console.log("Show workflow revisions key pressed") + if (!workflow.public) { + setShowWorkflowRevisions(true) + setSelectedRevision(workflow) + //setOriginalWorkflow(workflow) + } + } + + if (( event.ctrlKey || event.metaKey ) && event.key === ";") { + if (!workflow.public && executionModalOpen) { + getWorkflowExecution(props.match.params.key, ""); + } + } + + if (( event.ctrlKey || event.metaKey ) && event.shiftKey) { + console.log("Shift key pressed") + if (!workflow.public && executionModalOpen) { + setExecutionRunning(false); + stop() + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const newitem = removeParam("execution_id", cursearch); + navigate(curpath + newitem) + setExecutionModalView(0); + } + } + }; + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]); + return (
{ ) : ( - +
+ + { ) : (
- - { - setExecutionRunning(false); - stop(); - getWorkflowExecution(props.match.params.key, ""); - setExecutionModalView(0); - setLastExecution(executionData.execution_id); - }} + + - { - setExecutionRunning(false); - stop() - }} - > - - -

{ - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - const newitem = removeParam("execution_id", cursearch); - navigate(curpath + newitem) - setExecutionRunning(false); - stop() - }} - > - See more runs -

-
-
+ { + setExecutionRunning(false); + stop(); + getWorkflowExecution(props.match.params.key, ""); + setExecutionModalView(0); + setLastExecution(executionData.execution_id); + }} + > + { + setExecutionRunning(false); + stop() + }} + > + + + +

{ + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const newitem = removeParam("execution_id", cursearch); + navigate(curpath + newitem) + setExecutionRunning(false); + stop() + }} + > + See more runs +

+
+
+ { color="primary" title="Explore logs for the workflow" placement="top" - style={{ zIndex: 50000 }} + style={{ zIndex: 50000, }} >
@@ -1713,12 +1714,15 @@ const Dashboard = (props) => { {treeKeys.length > 0 ? diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index 2b0c1ebd..be99104c 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -13,6 +13,11 @@ import { //import { useAlert import { ToastContainer, toast } from "react-toastify" +import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material"; +import IconButton from "@mui/material/IconButton"; +import { Tooltip } from "@mui/material"; + + const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; //const alert = useAlert(); @@ -44,6 +49,17 @@ const Settings = (props) => { const [userSettings, setUserSettings] = useState({}); + const [showApiKey, setShowApiKey] = useState(false); + const [apiKeyCopied, setApiKeyCopied] = useState(false); + + const handleCopyApiKey = () => { + navigator.clipboard.writeText(userSettings.apikey); + setApiKeyCopied(true); + setTimeout(() => { + setApiKeyCopied(false); + }, 2000); + } + /* const [userdata.eth_info, setEthInfo] = useState(userdata.eth_info !== undefined && userdata.eth_info.account !== undefined && userdata.eth_info.account.length > 0 ? userdata.eth_info : { "account": "", @@ -467,7 +483,7 @@ const Settings = (props) => { > What is the API key used for? - { id="standard-required" margin="normal" variant="outlined" - /> + /> */} + + + setShowApiKey(!showApiKey)} + > + {showApiKey ? : } + + + + + + + + + ), + }} + color="primary" + value={showApiKey ? userSettings.apikey : '*'.repeat(36)} // Show API key if showApiKey is true, else show asterisks + required + disabled + fullWidth + placeholder="APIKEY" + id="standard-required" + margin="normal" + variant="outlined" + />

{passwordFormMessage}

- -

Creator Incentive Program

+ {isCloud && ( + <> + +

Creator Incentive Program

+ + )} +
{isCloud ? diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 0cff5f0f..f453c98a 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -551,9 +551,7 @@ const Workflows = (props) => { const [field1, setField1] = React.useState(""); const [field2, setField2] = React.useState(""); - const [downloadUrl, setDownloadUrl] = React.useState( - "https://github.com/frikky/shuffle-workflows" - ); + const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows") const [downloadBranch, setDownloadBranch] = React.useState("master"); const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = React.useState(false); @@ -924,7 +922,7 @@ const Workflows = (props) => { >
- Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
+ Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId)?.name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
@@ -3630,7 +3628,7 @@ const Workflows = (props) => { const handleGithubValidation = () => { importWorkflowsFromUrl(downloadUrl); setLoadWorkflowsModalOpen(false); - }; + } const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( { }, }} onChange={(e) => setDownloadUrl(e.target.value)} - placeholder="https://github.com/frikky/shuffle-apps" + placeholder="https://github.com/shuffle/workflows" fullWidth /> - Branch (default value is "master"): + Branch (default value is "main"):