diff --git a/.env b/.env
old mode 100644
new mode 100755
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
old mode 100644
new mode 100755
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
old mode 100644
new mode 100755
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
old mode 100644
new mode 100755
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
old mode 100644
new mode 100755
diff --git a/.github/install-guide.html b/.github/install-guide.html
old mode 100644
new mode 100755
diff --git a/.github/install-guide.md b/.github/install-guide.md
old mode 100644
new mode 100755
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
old mode 100644
new mode 100755
diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml
old mode 100644
new mode 100755
diff --git a/.github/workflows/snyk-container-analysis.yml b/.github/workflows/snyk-container-analysis.yml
old mode 100644
new mode 100755
diff --git a/.github/workflows/snyk-infrastructure-analysis.yml b/.github/workflows/snyk-infrastructure-analysis.yml
old mode 100644
new mode 100755
diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
diff --git a/SECURITY.md b/SECURITY.md
old mode 100644
new mode 100755
diff --git a/backend/Dockerfile b/backend/Dockerfile
old mode 100644
new mode 100755
diff --git a/backend/README.md b/backend/README.md
old mode 100644
new mode 100755
diff --git a/backend/app_gen/LICENSE b/backend/app_gen/LICENSE
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi-parsers/misp.py b/backend/app_gen/openapi-parsers/misp.py
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi-parsers/swimlane.py b/backend/app_gen/openapi-parsers/swimlane.py
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi/README.md b/backend/app_gen/openapi/README.md
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi/baseline/Dockerfile b/backend/app_gen/openapi/baseline/Dockerfile
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi/baseline/requirements.txt b/backend/app_gen/openapi/baseline/requirements.txt
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi/test.go b/backend/app_gen/openapi/test.go
old mode 100644
new mode 100755
diff --git a/backend/app_gen/openapi/testGCP.go b/backend/app_gen/openapi/testGCP.go
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/README.md b/backend/app_gen/python-lib/README.md
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/baseline/Dockerfile b/backend/app_gen/python-lib/baseline/Dockerfile
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/baseline/docker-compose.yml b/backend/app_gen/python-lib/baseline/docker-compose.yml
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/baseline/env.txt b/backend/app_gen/python-lib/baseline/env.txt
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/baseline/requirements.txt b/backend/app_gen/python-lib/baseline/requirements.txt
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/generator.py b/backend/app_gen/python-lib/generator.py
old mode 100644
new mode 100755
diff --git a/backend/app_gen/python-lib/requirements.txt b/backend/app_gen/python-lib/requirements.txt
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/Dockerfile_blackarch b/backend/app_sdk/Dockerfile_blackarch
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/Dockerfile_kali b/backend/app_sdk/Dockerfile_kali
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/LICENSE b/backend/app_sdk/LICENSE
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/README.md b/backend/app_sdk/README.md
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/__init__.py b/backend/app_sdk/__init__.py
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh
old mode 100644
new mode 100755
diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt
old mode 100644
new mode 100755
diff --git a/backend/build.sh b/backend/build.sh
old mode 100644
new mode 100755
diff --git a/backend/database/opensearch/docker-compose.yml b/backend/database/opensearch/docker-compose.yml
old mode 100644
new mode 100755
diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go
old mode 100644
new mode 100755
index 8cb07ff6..a6324faf
--- a/backend/go-app/docker.go
+++ b/backend/go-app/docker.go
@@ -534,7 +534,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) {
if len(img.ID) == 0 {
if len(img2.ID) == 0 {
workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 0, 0)
- //log.Printf("[INFO] Getting workflowapps for a rebuild. Got %d with err %#v", len(workflowapps), err)
+ log.Printf("[INFO] Getting workflowapps for a rebuild. Got %d with err %#v", len(workflowapps), err)
if err == nil {
imageName := ""
imageVersion := ""
diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod
old mode 100644
new mode 100755
index 7a0e631e..b43db79b
--- a/backend/go-app/go.mod
+++ b/backend/go-app/go.mod
@@ -49,7 +49,6 @@ 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/go-elasticsearch/v8 v8.13.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
@@ -102,4 +101,5 @@ require (
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
+
)
diff --git a/backend/go-app/main.go b/backend/go-app/main.go
old mode 100644
new mode 100755
index 3782d239..b1304b4f
--- a/backend/go-app/main.go
+++ b/backend/go-app/main.go
@@ -5997,6 +5997,7 @@ func initHandlers() {
// New for recommendations in Shuffle
r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS")
+ r.HandleFunc("/api/v1/workflows/{key}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS")
// Triggers
r.HandleFunc("/api/v1/hooks/new", shuffle.HandleNewHook).Methods("POST", "OPTIONS")
diff --git a/backend/go-app/main_test.go b/backend/go-app/main_test.go
old mode 100644
new mode 100755
diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go
old mode 100644
new mode 100755
index 631c653d..2f057c8e
--- a/backend/go-app/walkoff.go
+++ b/backend/go-app/walkoff.go
@@ -636,6 +636,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
}
//log.Printf("Actionresult unmarshal: %s", string(body))
+ log.Printf("[DEBUG] Got workflow result from %s of length %d", request.RemoteAddr, len(body))
ctx := context.Background()
err = shuffle.ValidateNewWorkerExecution(ctx, body)
if err == nil {
@@ -774,7 +775,6 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
}
//log.Printf("BASE LENGTH: %d", len(workflowExecution.Results))
-
workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, false, 0)
if err != nil {
b, suberr := json.Marshal(actionResult)
@@ -1118,6 +1118,678 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
err = imageCheckBuilder(execInfo.ImageNames)
if err != nil {
log.Printf("[ERROR] Failed building the required images from %#v: %s", execInfo.ImageNames, err)
+ return shuffle.WorkflowExecution{}, "Failed unmarshal during execution", err
+ }
+
+ makeNew := true
+ start, startok := request.URL.Query()["start"]
+ if request.Method == "POST" {
+ body, err := ioutil.ReadAll(request.Body)
+ if err != nil {
+ log.Printf("[ERROR] Failed request POST read: %s", err)
+ return shuffle.WorkflowExecution{}, "Failed getting body", err
+ }
+
+ // This one doesn't really matter.
+ log.Printf("[INFO] Running POST execution with body of length %d for workflow %s", len(string(body)), workflowExecution.Workflow.ID)
+
+ if len(body) >= 4 {
+ if body[0] == 34 && body[len(body)-1] == 34 {
+ body = body[1 : len(body)-1]
+ }
+ if body[0] == 34 && body[len(body)-1] == 34 {
+ body = body[1 : len(body)-1]
+ }
+ }
+
+ sourceAuth, sourceAuthOk := request.URL.Query()["source_auth"]
+ if sourceAuthOk {
+ //log.Printf("\n\n\nSETTING SOURCE WORKFLOW AUTH TO %s!!!\n\n\n", sourceAuth[0])
+ workflowExecution.ExecutionSourceAuth = sourceAuth[0]
+ } else {
+ //log.Printf("Did NOT get source workflow")
+ }
+
+ sourceNode, sourceNodeOk := request.URL.Query()["source_node"]
+ if sourceNodeOk {
+ //log.Printf("\n\n\nSETTING SOURCE WORKFLOW NODE TO %s!!!\n\n\n", sourceNode[0])
+ workflowExecution.ExecutionSourceNode = sourceNode[0]
+ } else {
+ //log.Printf("Did NOT get source workflow")
+ }
+
+ //workflowExecution.ExecutionSource = "default"
+ sourceWorkflow, sourceWorkflowOk := request.URL.Query()["source_workflow"]
+ if sourceWorkflowOk {
+ //log.Printf("Got source workflow %s", sourceWorkflow)
+ workflowExecution.ExecutionSource = sourceWorkflow[0]
+ } else {
+ //log.Printf("Did NOT get source workflow")
+ }
+
+ sourceExecution, sourceExecutionOk := request.URL.Query()["source_execution"]
+ if sourceExecutionOk {
+ //log.Printf("[INFO] Got source execution%s", sourceExecution)
+ workflowExecution.ExecutionParent = sourceExecution[0]
+ } else {
+ //log.Printf("Did NOT get source execution")
+ }
+
+ if len(string(body)) < 50 {
+ //log.Println(body)
+ // String in string
+ //log.Println(body)
+
+ //if string(body)[0] == "\"" && string(body)[string(body)
+ log.Printf("[DEBUG] Body: %s", string(body))
+ }
+
+ var execution shuffle.ExecutionRequest
+ err = json.Unmarshal(body, &execution)
+ if err != nil {
+ log.Printf("[WARNING] Failed execution POST unmarshaling - continuing anyway: %s", err)
+ //return shuffle.WorkflowExecution{}, "", err
+ }
+
+ if execution.Start == "" && len(body) > 0 {
+ execution.ExecutionArgument = string(body)
+ }
+
+ // FIXME - this should have "execution_argument" from executeWorkflow frontend
+ //log.Printf("EXEC: %#v", execution)
+ if len(execution.ExecutionArgument) > 0 {
+ workflowExecution.ExecutionArgument = execution.ExecutionArgument
+ }
+
+ if len(execution.ExecutionSource) > 0 {
+ workflowExecution.ExecutionSource = execution.ExecutionSource
+ }
+
+ //log.Printf("Execution data: %#v", execution)
+ if len(execution.Start) == 36 && len(workflow.Actions) > 0 {
+ log.Printf("[INFO] Should start execution on node %s", execution.Start)
+ workflowExecution.Start = execution.Start
+
+ found := false
+ for _, action := range workflow.Actions {
+ if action.ID == execution.Start {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ log.Printf("[ERROR] Action %s was NOT found! Exiting execution.", execution.Start)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode %s was not found in actions", workflow.Start), errors.New(fmt.Sprintf("Startnode %s was not found in actions", workflow.Start))
+ }
+ } else if len(execution.Start) > 0 {
+ //log.Printf("[INFO] !")
+ //log.Printf("[ERROR] START ACTION %s IS WRONG ID LENGTH %d!", execution.Start, len(execution.Start))
+ //return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode %s was not found in actions", execution.Start), errors.New(fmt.Sprintf("Startnode %s was not found in actions", execution.Start))
+ }
+
+ if len(execution.ExecutionId) == 36 {
+ workflowExecution.ExecutionId = execution.ExecutionId
+ } else {
+ sessionToken := uuid.NewV4()
+ workflowExecution.ExecutionId = sessionToken.String()
+ }
+ } else {
+ // Check for parameters of start and ExecutionId
+ // This is mostly used for user input trigger
+
+ answer, answerok := request.URL.Query()["answer"]
+ referenceId, referenceok := request.URL.Query()["reference_execution"]
+ if answerok && referenceok {
+ // If answer is false, reference execution with result
+ log.Printf("[INFO] Answer is OK AND reference is OK!")
+ if answer[0] == "false" {
+ log.Printf("Should update reference and return, no need for further execution!")
+
+ // Get the reference execution
+ oldExecution, err := shuffle.GetWorkflowExecution(ctx, referenceId[0])
+ if err != nil {
+ log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
+ }
+
+ if oldExecution.Workflow.ID != id {
+ log.Println("Wrong workflowid!")
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Bad ID %s", referenceId), errors.New("Bad ID")
+ }
+
+ newResults := []shuffle.ActionResult{}
+ //log.Printf("%#v", oldExecution.Results)
+ for _, result := range oldExecution.Results {
+ log.Printf("%s - %s", result.Action.ID, start[0])
+ if result.Action.ID == start[0] {
+ note, noteok := request.URL.Query()["note"]
+ if noteok {
+ result.Result = fmt.Sprintf("User note: %s", note[0])
+ } else {
+ result.Result = fmt.Sprintf("User clicked %s", answer[0])
+ }
+
+ // Stopping the whole thing
+ result.CompletedAt = int64(time.Now().Unix())
+ result.Status = "ABORTED"
+ oldExecution.Status = result.Status
+ oldExecution.Result = result.Result
+ oldExecution.LastNode = result.Action.ID
+ }
+
+ newResults = append(newResults, result)
+ }
+
+ oldExecution.Results = newResults
+ err = shuffle.SetWorkflowExecution(ctx, *oldExecution, true)
+ if err != nil {
+ log.Printf("Error saving workflow execution actionresult setting: %s", err)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed setting workflowexecution actionresult in execution: %s", err), err
+ }
+
+ return shuffle.WorkflowExecution{}, "", nil
+ }
+ }
+
+ if referenceok {
+ log.Printf("Handling an old execution continuation!")
+ // Will use the old name, but still continue with NEW ID
+ oldExecution, err := shuffle.GetWorkflowExecution(ctx, referenceId[0])
+ if err != nil {
+ log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
+ }
+
+ workflowExecution = *oldExecution
+ }
+
+ if len(workflowExecution.ExecutionId) == 0 {
+ sessionToken := uuid.NewV4()
+ workflowExecution.ExecutionId = sessionToken.String()
+ } else {
+ log.Printf("Using the same executionId as before: %s", workflowExecution.ExecutionId)
+ makeNew = false
+ }
+
+ // Don't override workflow defaults
+ }
+
+ if startok {
+ //log.Printf("\n\n[INFO] Setting start to %s based on query!\n\n", start[0])
+ //workflowExecution.Workflow.Start = start[0]
+ workflowExecution.Start = start[0]
+ }
+
+ // FIXME - regex uuid, and check if already exists?
+ if len(workflowExecution.ExecutionId) != 36 {
+ log.Printf("Invalid uuid: %s", workflowExecution.ExecutionId)
+ return shuffle.WorkflowExecution{}, "Invalid uuid", err
+ }
+
+ // FIXME - find owner of workflow
+ // FIXME - get the actual workflow itself and build the request
+ // MAYBE: Don't send the workflow within the pubsub, as this requires more data to be sent
+ // Check if a worker already exists for company, else run one with:
+ // locations, project IDs and subscription names
+
+ // When app is executed:
+ // Should update with status execution (somewhere), which will trigger the next node
+ // IF action.type == internal, we need the internal watcher to be running and executing
+ // This essentially means the WORKER has to be the responsible party for new actions in the INTERNAL landscape
+ // Results are ALWAYS posted back to cloud@execution_id?
+ if makeNew {
+ workflowExecution.Type = "workflow"
+ //workflowExecution.Stream = "tmp"
+ //workflowExecution.WorkflowQueue = "tmp"
+ //workflowExecution.SubscriptionNameNodestream = "testcompany-nodestream"
+ //workflowExecution.Locations = []string{"europe-west2"}
+ workflowExecution.ProjectId = gceProject
+ workflowExecution.WorkflowId = workflow.ID
+ workflowExecution.StartedAt = int64(time.Now().Unix())
+ workflowExecution.CompletedAt = 0
+ workflowExecution.Authorization = uuid.NewV4().String()
+
+ // Status for the entire workflow.
+ workflowExecution.Status = "EXECUTING"
+ }
+
+ if len(workflowExecution.ExecutionSource) == 0 {
+ log.Printf("[INFO] No execution source (trigger) specified. Setting to default")
+ workflowExecution.ExecutionSource = "default"
+ } else {
+ log.Printf("[INFO] Execution source is %s for execution ID %s in workflow %s", workflowExecution.ExecutionSource, workflowExecution.ExecutionId, workflowExecution.Workflow.ID)
+ }
+
+ workflowExecution.ExecutionVariables = workflow.ExecutionVariables
+ if len(workflowExecution.Start) == 0 && len(workflowExecution.Workflow.Start) > 0 {
+ workflowExecution.Start = workflowExecution.Workflow.Start
+ }
+
+ startnodeFound := false
+ newStartnode := ""
+ for _, item := range workflowExecution.Workflow.Actions {
+ if item.ID == workflowExecution.Start {
+ startnodeFound = true
+ }
+
+ if item.IsStartNode {
+ newStartnode = item.ID
+ }
+ }
+
+ if !startnodeFound {
+ log.Printf("[INFO] Couldn't find startnode %s. Remapping to %#v", workflowExecution.Start, newStartnode)
+
+ if len(newStartnode) > 0 {
+ workflowExecution.Start = newStartnode
+ } else {
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode couldn't be found"), errors.New("Startnode isn't defined in this workflow..")
+ }
+ }
+
+ childNodes := shuffle.FindChildNodes(workflowExecution, workflowExecution.Start, []string{}, []string{})
+
+ topic := "workflows"
+ startFound := false
+ // FIXME - remove this?
+ newActions := []shuffle.Action{}
+ defaultResults := []shuffle.ActionResult{}
+
+ allAuths := []shuffle.AppAuthenticationStorage{}
+ for _, action := range workflowExecution.Workflow.Actions {
+ //action.LargeImage = ""
+ if action.ID == workflowExecution.Start {
+ startFound = true
+ }
+ //log.Println(action.Environment)
+
+ if action.Environment == "" {
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!")
+ }
+
+ // FIXME: Authentication parameters
+ if len(action.AuthenticationId) > 0 {
+ if len(allAuths) == 0 {
+ allAuths, err = shuffle.GetAllWorkflowAppAuth(ctx, workflow.ExecutingOrg.Id)
+ if err != nil {
+ log.Printf("Api authentication failed in get all app auth: %s", err)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Api authentication failed in get all app auth: %s", err), err
+ }
+ }
+
+ curAuth := shuffle.AppAuthenticationStorage{Id: ""}
+ for _, auth := range allAuths {
+ if auth.Id == action.AuthenticationId {
+ curAuth = auth
+ break
+ }
+ }
+
+ if len(curAuth.Id) == 0 {
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId), errors.New(fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId))
+ }
+
+ if curAuth.Encrypted {
+ setField := true
+ newFields := []shuffle.AuthenticationStore{}
+ for _, field := range curAuth.Fields {
+ parsedKey := fmt.Sprintf("%s_%d_%s_%s", curAuth.OrgId, curAuth.Created, curAuth.Label, field.Key)
+ newValue, err := shuffle.HandleKeyDecryption([]byte(field.Value), parsedKey)
+ if err != nil {
+ log.Printf("[WARNING] Failed decryption for %s: %s", field.Key, err)
+ setField = false
+ break
+ }
+
+ field.Value = string(newValue)
+ newFields = append(newFields, field)
+ }
+
+ if setField {
+ curAuth.Fields = newFields
+ }
+ } else {
+ log.Printf("[INFO] AUTH IS NOT ENCRYPTED - attempting encrypting!")
+ err = shuffle.SetWorkflowAppAuthDatastore(ctx, curAuth, curAuth.Id)
+ if err != nil {
+ log.Printf("[WARNING] Failed running encryption during execution: %s", err)
+ }
+ }
+
+ newParams := []shuffle.WorkflowAppActionParameter{}
+ if strings.ToLower(curAuth.Type) == "oauth2" {
+ log.Printf("[DEBUG] Should replace auth parameters (Oauth2)")
+
+ for _, param := range curAuth.Fields {
+ if param.Key == "expiration" {
+ continue
+ }
+
+ newParams = append(newParams, shuffle.WorkflowAppActionParameter{
+ Name: param.Key,
+ Value: param.Value,
+ })
+ }
+
+ for _, param := range action.Parameters {
+ //log.Printf("Param: %#v", param)
+ if param.Configuration {
+ continue
+ }
+
+ newParams = append(newParams, param)
+ }
+ } else {
+ // Rebuild params with the right data. This is to prevent issues on the frontend
+ for _, param := range action.Parameters {
+
+ for _, authparam := range curAuth.Fields {
+ if param.Name == authparam.Key {
+ param.Value = authparam.Value
+ //log.Printf("Name: %s - value: %s", param.Name, param.Value)
+ //log.Printf("Name: %s - value: %s\n", param.Name, param.Value)
+ break
+ }
+ }
+
+ newParams = append(newParams, param)
+ }
+ }
+
+ action.Parameters = newParams
+ }
+
+ action.LargeImage = ""
+ if len(action.Label) == 0 {
+ action.Label = action.ID
+ }
+ //log.Printf("LABEL: %s", action.Label)
+ newActions = append(newActions, action)
+
+ // If the node is NOT found, it's supposed to be set to SKIPPED,
+ // as it's not a childnode of the startnode
+ // This is a configuration item for the workflow itself.
+ if len(workflowExecution.Results) > 0 {
+ defaultResults = []shuffle.ActionResult{}
+ for _, result := range workflowExecution.Results {
+ if result.Status == "WAITING" {
+ result.Status = "FINISHED"
+ result.Result = "Continuing"
+ }
+
+ defaultResults = append(defaultResults, result)
+ }
+ } else if len(workflowExecution.Results) == 0 && !workflowExecution.Workflow.Configuration.StartFromTop {
+ found := false
+ for _, nodeId := range childNodes {
+ if nodeId == action.ID {
+ //log.Printf("Found %s", action.ID)
+ found = true
+ }
+ }
+
+ if !found {
+ if action.ID == workflowExecution.Start {
+ continue
+ }
+
+ //log.Printf("[WARNING] Set %s to SKIPPED as it's NOT a childnode of the startnode.", action.ID)
+ curaction := shuffle.Action{
+ AppName: action.AppName,
+ AppVersion: action.AppVersion,
+ Label: action.Label,
+ Name: action.Name,
+ ID: action.ID,
+ }
+ //action
+ //curaction.Parameters = []
+ defaultResults = append(defaultResults, shuffle.ActionResult{
+ Action: curaction,
+ ExecutionId: workflowExecution.ExecutionId,
+ Authorization: workflowExecution.Authorization,
+ Result: "Skipped because it's not under the startnode",
+ StartedAt: 0,
+ CompletedAt: 0,
+ Status: "SKIPPED",
+ })
+ }
+ }
+ }
+
+ removeTriggers := []string{}
+ for triggerIndex, trigger := range workflowExecution.Workflow.Triggers {
+ //log.Printf("[INFO] ID: %s vs %s", trigger.ID, workflowExecution.Start)
+ if trigger.ID == workflowExecution.Start {
+ if trigger.AppName == "User Input" {
+ startFound = true
+ break
+ }
+ }
+
+ if trigger.AppName == "User Input" || trigger.AppName == "Shuffle Workflow" {
+ found := false
+ for _, node := range childNodes {
+ if node == trigger.ID {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ //log.Printf("SHOULD SET TRIGGER %s TO BE SKIPPED", trigger.ID)
+
+ curaction := shuffle.Action{
+ AppName: "shuffle-subflow",
+ AppVersion: trigger.AppVersion,
+ Label: trigger.Label,
+ Name: trigger.Name,
+ ID: trigger.ID,
+ }
+
+ defaultResults = append(defaultResults, shuffle.ActionResult{
+ Action: curaction,
+ ExecutionId: workflowExecution.ExecutionId,
+ Authorization: workflowExecution.Authorization,
+ Result: "Skipped because it's not under the startnode",
+ StartedAt: 0,
+ CompletedAt: 0,
+ Status: "SKIPPED",
+ })
+ } else {
+ // Replaces trigger with the subflow
+ //if trigger.AppName == "Shuffle Workflow" {
+ // replaceActions := false
+ // workflowAction := ""
+ // for _, param := range trigger.Parameters {
+ // if param.Name == "argument" && !strings.Contains(param.Value, ".#") {
+ // replaceActions = true
+ // }
+
+ // if param.Name == "startnode" {
+ // workflowAction = param.Value
+ // }
+ // }
+
+ // if replaceActions {
+ // replacementNodes, newBranches, lastnode := shuffle.GetReplacementNodes(ctx, workflowExecution, trigger, trigger.Label)
+ // log.Printf("REPLACEMENTS: %d, %d", len(replacementNodes), len(newBranches))
+ // if len(replacementNodes) > 0 {
+ // for _, action := range replacementNodes {
+ // found := false
+
+ // for subActionIndex, subaction := range newActions {
+ // if subaction.ID == action.ID {
+ // found = true
+ // //newActions[subActionIndex].Name = action.Name
+ // newActions[subActionIndex].Label = action.Label
+ // break
+ // }
+ // }
+
+ // if !found {
+ // action.SubAction = true
+ // newActions = append(newActions, action)
+ // }
+
+ // // Check if it's already set to have a value
+ // for resultIndex, result := range defaultResults {
+ // if result.Action.ID == action.ID {
+ // defaultResults = append(defaultResults[:resultIndex], defaultResults[resultIndex+1:]...)
+ // break
+ // }
+ // }
+ // }
+
+ // for _, branch := range newBranches {
+ // workflowExecution.Workflow.Branches = append(workflowExecution.Workflow.Branches, branch)
+ // }
+
+ // // Append branches:
+ // // parent -> new inner node (FIRST one)
+ // for branchIndex, branch := range workflowExecution.Workflow.Branches {
+ // if branch.DestinationID == trigger.ID {
+ // log.Printf("REPLACE DESTINATION WITH %s!!", workflowAction)
+ // workflowExecution.Workflow.Branches[branchIndex].DestinationID = workflowAction
+ // }
+
+ // if branch.SourceID == trigger.ID {
+ // log.Printf("REPLACE SOURCE WITH LASTNODE %s!!", lastnode)
+ // workflowExecution.Workflow.Branches[branchIndex].SourceID = lastnode
+ // }
+ // }
+
+ // // Remove the trigger
+ // removeTriggers = append(removeTriggers, workflowExecution.Workflow.Triggers[triggerIndex].ID)
+ // }
+
+ // log.Printf("NEW ACTION LENGTH %d, RESULT: %d, Triggers: %d, BRANCHES: %d", len(newActions), len(defaultResults), len(workflowExecution.Workflow.Triggers), len(workflowExecution.Workflow.Branches))
+ // }
+ //}
+ _ = triggerIndex
+ }
+ }
+ }
+
+ //newTriggers := []shuffle.Trigger{}
+ //for _, trigger := range workflowExecution.Workflow.Triggers {
+ // found := false
+ // for _, triggerId := range removeTriggers {
+ // if trigger.ID == triggerId {
+ // found = true
+ // break
+ // }
+ // }
+
+ // if found {
+ // log.Printf("[WARNING] Removed trigger %s during execution", trigger.ID)
+ // continue
+ // }
+
+ // newTriggers = append(newTriggers, trigger)
+ //}
+ //workflowExecution.Workflow.Triggers = newTriggers
+ _ = removeTriggers
+
+ if !startFound {
+ if len(workflowExecution.Start) == 0 && len(workflowExecution.Workflow.Start) > 0 {
+ workflowExecution.Start = workflow.Start
+ } else if len(workflowExecution.Workflow.Actions) > 0 {
+ workflowExecution.Start = workflowExecution.Workflow.Actions[0].ID
+ } else {
+ log.Printf("[ERROR] Startnode %s doesn't exist!!", workflowExecution.Start)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Workflow action %s doesn't exist in workflow", workflowExecution.Start), errors.New(fmt.Sprintf(`Workflow start node "%s" doesn't exist. Exiting!`, workflowExecution.Start))
+ }
+ }
+
+ //log.Printf("EXECUTION START: %s", workflowExecution.Start)
+
+ // Verification for execution environments
+ workflowExecution.Results = defaultResults
+ workflowExecution.Workflow.Actions = newActions
+ onpremExecution := true
+ environments := []string{}
+
+ if len(workflowExecution.ExecutionOrg) == 0 && len(workflow.ExecutingOrg.Id) > 0 {
+ workflowExecution.ExecutionOrg = workflow.ExecutingOrg.Id
+ }
+
+ var allEnvs []shuffle.Environment
+ if len(workflowExecution.ExecutionOrg) > 0 {
+ //log.Printf("[INFO] Executing ORG: %s", workflowExecution.ExecutionOrg)
+
+ allEnvironments, err := shuffle.GetEnvironments(ctx, workflowExecution.ExecutionOrg)
+ if err != nil {
+ log.Printf("Failed finding environments: %s", err)
+ return shuffle.WorkflowExecution{}, fmt.Sprintf("Workflow environments not found for this org"), errors.New(fmt.Sprintf("Workflow environments not found for this org"))
+ }
+
+ for _, curenv := range allEnvironments {
+ if curenv.Archived {
+ continue
+ }
+
+ allEnvs = append(allEnvs, curenv)
+ }
+ } else {
+ log.Printf("[ERROR] No org identified for execution of %s. Returning", workflowExecution.Workflow.ID)
+ return shuffle.WorkflowExecution{}, "No org identified for execution", errors.New("No org identified for execution")
+ }
+
+ if len(allEnvs) == 0 {
+ log.Printf("[ERROR] No active environments found for org: %s", workflowExecution.ExecutionOrg)
+ return shuffle.WorkflowExecution{}, "No active environments found", errors.New(fmt.Sprintf("No active env found for org %s", workflowExecution.ExecutionOrg))
+ }
+
+ // Check if the actions are children of the startnode?
+ imageNames := []string{}
+ cloudExec := false
+ for _, action := range workflowExecution.Workflow.Actions {
+ // Verify if the action environment exists and append
+ found := false
+ for _, env := range allEnvs {
+ if env.Name == action.Environment {
+ found = true
+
+ if env.Type == "cloud" {
+ cloudExec = true
+ } else if env.Type == "onprem" {
+ onpremExecution = true
+ } else {
+ log.Printf("[ERROR] No handler for environment type %s", env.Type)
+ return shuffle.WorkflowExecution{}, "No active environments found", errors.New(fmt.Sprintf("No handler for environment type %s", env.Type))
+ }
+ break
+ }
+ }
+
+ if !found {
+ log.Printf("[ERROR] Couldn't find environment %s. Maybe it's inactive?", action.Environment)
+ return shuffle.WorkflowExecution{}, "Couldn't find the environment", errors.New(fmt.Sprintf("Couldn't find env %s in org %s", action.Environment, workflowExecution.ExecutionOrg))
+ }
+
+ found = false
+ for _, env := range environments {
+ if env == action.Environment {
+
+ found = true
+ break
+ }
+ }
+
+ // Check if the app exists?
+ newName := action.AppName
+ newName = strings.ReplaceAll(newName, " ", "-")
+ imageNames = append(imageNames, fmt.Sprintf("%s:%s_%s", baseDockerName, newName, action.AppVersion))
+
+ if !found {
+ environments = append(environments, action.Environment)
+ }
+ }
+
+ err = imageCheckBuilder(imageNames)
+ if err != nil {
+ log.Printf("[ERROR] Failed building the required images from %#v: %s", imageNames, err)
return shuffle.WorkflowExecution{}, "Failed building missing Docker images", err
}
@@ -2867,7 +3539,7 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) {
}
resp.WriteHeader(200)
- resp.Write(returnBytes)
+ resp.Write([]byte(returnBytes))
}
// Onlyname is used to
diff --git a/backend/tests/cache.sh b/backend/tests/cache.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/cleanup.sh b/backend/tests/cleanup.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/dockerpull.sh b/backend/tests/dockerpull.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/execute.sh b/backend/tests/execute.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/file_download.sh b/backend/tests/file_download.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/forparser.py b/backend/tests/forparser.py
old mode 100644
new mode 100755
diff --git a/backend/tests/hooks.sh b/backend/tests/hooks.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/hotload.sh b/backend/tests/hotload.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/list_files.sh b/backend/tests/list_files.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/migrate_db.sh b/backend/tests/migrate_db.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/run_function.py b/backend/tests/run_function.py
old mode 100644
new mode 100755
diff --git a/backend/tests/scheduleapps.sh b/backend/tests/scheduleapps.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/schedules.sh b/backend/tests/schedules.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/sendmail.sh b/backend/tests/sendmail.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/testWorkflows.sh b/backend/tests/testWorkflows.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/test_wrappers.py b/backend/tests/test_wrappers.py
old mode 100644
new mode 100755
diff --git a/backend/tests/triggers.sh b/backend/tests/triggers.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/users.sh b/backend/tests/users.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/validate_app_values.sh b/backend/tests/validate_app_values.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/websocket.sh b/backend/tests/websocket.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/workflowdata.json b/backend/tests/workflowdata.json
old mode 100644
new mode 100755
diff --git a/backend/tests/workflowresults.sh b/backend/tests/workflowresults.sh
old mode 100644
new mode 100755
diff --git a/backend/tests/workflows.sh b/backend/tests/workflows.sh
old mode 100644
new mode 100755
diff --git a/docker-compose.yml b/docker-compose.yml
old mode 100644
new mode 100755
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
old mode 100644
new mode 100755
diff --git a/frontend/README.md b/frontend/README.md
old mode 100644
new mode 100755
diff --git a/frontend/build.sh b/frontend/build.sh
old mode 100644
new mode 100755
diff --git a/frontend/certs/certreq.csr b/frontend/certs/certreq.csr
old mode 100644
new mode 100755
diff --git a/frontend/certs/fullchain.pem b/frontend/certs/fullchain.pem
old mode 100644
new mode 100755
diff --git a/frontend/certs/privkey.pem b/frontend/certs/privkey.pem
old mode 100644
new mode 100755
diff --git a/frontend/confd/conf.d/nginx.conf.toml b/frontend/confd/conf.d/nginx.conf.toml
old mode 100644
new mode 100755
diff --git a/frontend/confd/templates/nginx.conf b/frontend/confd/templates/nginx.conf
old mode 100644
new mode 100755
diff --git a/frontend/package.json b/frontend/package.json
old mode 100644
new mode 100755
index 600cb60f..7e25c17d
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,10 +1,9 @@
{
"name": "shuffler",
"homepage": "https://shuffler.io",
- "version": "1.1.4",
+ "version": "1.2.0",
"private": true,
"dependencies": {
- "@babel/core": "^7.15.8",
"@codemirror/commands": "^6.2.2",
"@emotion/is-prop-valid": "^1.1.1",
"@emotion/react": "^11.7.0",
@@ -23,7 +22,6 @@
"@uiw/react-codemirror": "^3.2.1",
"@use-it/interval": "^1.0.0",
"algoliasearch": "^4.13.1",
- "babel-eslint": "^10.1.0",
"class-transformer": "^0.4.0",
"create-react-app": "^4.0.3",
"cytoscape": "^3.15.1",
@@ -79,14 +77,12 @@
"shellwords": "^0.1.1",
"simplebar": "^4.2.3",
"styled-components": "^4.4.0",
- "webpack": "^4.44.2",
- "websocket": "^1.0.30",
"yaml": "^1.7.2",
"yamljs": "^0.3.0",
"zone.js": "~0.11.4"
},
"scripts": {
- "start": "PORT=3000 react-scripts start",
+ "start": "HTTPS=false&&PORT=3000 react-scripts --openssl-legacy-provider start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
@@ -110,5 +106,9 @@
"devDependencies": {
"prettier": "2.4.1",
"promise-window": "^1.2.1"
+ "@babel/core": "^7.15.8",
+ "babel-eslint": "^10.1.0",
+ "webpack": "^4.44.2",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}
diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico
old mode 100644
new mode 100755
diff --git a/frontend/public/images/Shuffle_logo.png b/frontend/public/images/Shuffle_logo.png
old mode 100644
new mode 100755
diff --git a/frontend/public/index.html b/frontend/public/index.html
old mode 100644
new mode 100755
diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
old mode 100644
new mode 100755
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
old mode 100644
new mode 100755
diff --git a/frontend/src/__test__/appdata.js b/frontend/src/__test__/appdata.js
old mode 100644
new mode 100755
diff --git a/frontend/src/__test__/environmentdata.js b/frontend/src/__test__/environmentdata.js
old mode 100644
new mode 100755
diff --git a/frontend/src/__test__/scheduledata.js b/frontend/src/__test__/scheduledata.js
old mode 100644
new mode 100755
diff --git a/frontend/src/__test__/webhookdata.js b/frontend/src/__test__/webhookdata.js
old mode 100644
new mode 100755
diff --git a/frontend/src/__test__/workflowdata.js b/frontend/src/__test__/workflowdata.js
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/bag.svg b/frontend/src/assets/img/bag.svg
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/book.svg b/frontend/src/assets/img/book.svg
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/github_phishing_email.png b/frontend/src/assets/img/github_phishing_email.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/github_shuffle_img.png b/frontend/src/assets/img/github_shuffle_img.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/icpl_logo.png b/frontend/src/assets/img/icpl_logo.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/kafka.png b/frontend/src/assets/img/kafka.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/logo.png b/frontend/src/assets/img/logo.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/logo.svg b/frontend/src/assets/img/logo.svg
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/logo.webp b/frontend/src/assets/img/logo.webp
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/mobile.svg b/frontend/src/assets/img/mobile.svg
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/shuffle_adminaccount.png b/frontend/src/assets/img/shuffle_adminaccount.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/shuffle_architecture.png b/frontend/src/assets/img/shuffle_architecture.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/shuffle_webhook.png b/frontend/src/assets/img/shuffle_webhook.png
old mode 100644
new mode 100755
diff --git a/frontend/src/assets/img/webhook.png b/frontend/src/assets/img/webhook.png
old mode 100644
new mode 100755
diff --git a/frontend/src/charts.js b/frontend/src/charts.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/AlertPopup.js b/frontend/src/components/AlertPopup.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/AlertTemplate.js b/frontend/src/components/AlertTemplate.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx
old mode 100644
new mode 100755
diff --git a/frontend/src/components/Dropzone.jsx b/frontend/src/components/Dropzone.jsx
old mode 100644
new mode 100755
diff --git a/frontend/src/components/FooterNew.js b/frontend/src/components/FooterNew.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/LoginPopup.js b/frontend/src/components/LoginPopup.js
old mode 100644
new mode 100755
diff --git a/frontend/src/components/NestedMenu.jsx b/frontend/src/components/NestedMenu.jsx
old mode 100644
new mode 100755
diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx
old mode 100644
new mode 100755
index c28f24c7..e33a8f1f
--- a/frontend/src/components/Oauth2Auth.jsx
+++ b/frontend/src/components/Oauth2Auth.jsx
@@ -2,6 +2,7 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
import { useParams, useNavigate, Link } from "react-router-dom";
import { useTheme } from "@material-ui/core/styles";
import theme from '../theme.jsx';
+import { useAlert } from "react-alert";
import { v4 as uuidv4 } from "uuid";
import {
@@ -97,6 +98,7 @@ const AuthenticationOauth2 = (props) => {
} = props;
let navigate = useNavigate();
+ const alert = useAlert()
//const [update, setUpdate] = React.useState("|")
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
@@ -282,6 +284,51 @@ const AuthenticationOauth2 = (props) => {
}
+ const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
+ setButtonClicked(true);
+ //console.log("SCOPES: ", scopes);
+
+ client_id = client_id.trim()
+ client_secret = client_secret.trim()
+ oauth_url = oauth_url.trim()
+
+ var resources = "";
+ if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
+ console.log("IN scope 1")
+ if (offlineAccess === true && !scopes.includes("offline_access")) {
+
+ console.log("IN scope 2")
+ if (!authenticationType.redirect_uri.includes("google")) {
+ console.log("Appending offline access")
+ scopes.push("offline_access")
+ }
+ }
+
+ resources = scopes.join(" ");
+ //resources = scopes.join(",");
+ }
+
+ const authentication_url = authenticationType.token_uri;
+ //console.log("AUTH: ", authenticationType)
+ //console.log("SCOPES2: ", resources)
+ const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
+ const workflowId = workflow !== undefined ? workflow.id : "";
+ var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
+
+
+ // This is to make sure authorization can be handled WITHOUT being logged in,
+ // kind of making it act like an api key
+ // https://shuffler.io/authorization -> 3rd party integration auth
+ const urlParams = new URLSearchParams(window.location.search);
+ const userAuth = urlParams.get("authorization");
+ if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
+ console.log("Adding authorization from user side")
+ state += `%26authorization%3d${userAuth}`;
+ }
+ // write:request:jira-service-management
+ }
+
+
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
setButtonClicked(true);
//console.log("SCOPES: ", scopes);
@@ -317,6 +364,35 @@ const AuthenticationOauth2 = (props) => {
console.log("ADDING OAUTH2 URL: ", state);
}
+ if (
+ authenticationType.refresh_uri !== undefined &&
+ authenticationType.refresh_uri !== null &&
+ authenticationType.refresh_uri.length > 0
+ ) {
+ state += `%26refresh_uri%3d${authenticationType.refresh_uri}`;
+ } else {
+ state += `%26refresh_uri%3d${authentication_url}`;
+ }
+
+ // No prompt forcing
+ //var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
+ var defaultPrompt = "login"
+ if (prompt !== undefined && prompt !== null && prompt.length > 0) {
+ defaultPrompt = prompt
+ }
+ // Check for org_id
+ const orgId = urlParams.get("org_id");
+ if (orgId !== undefined && orgId !== null && orgId.length > 0) {
+ console.log("Adding org_id from user side")
+ state += `%26org_id%3d${orgId}`;
+ }
+
+ if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
+ state += `%26oauth_url%3d${oauth_url}`;
+ console.log("ADDING OAUTH2 URL: ", state);
+ }
+
+
if (
authenticationType.refresh_uri !== undefined &&
authenticationType.refresh_uri !== null &&
@@ -356,7 +432,7 @@ const AuthenticationOauth2 = (props) => {
// How can we get a callback properly realtime?
// How can we properly try-catch without breaks on error?
try {
- var newwin = window.open(url, "", "width=800,height=600");
+ var newwin = window.open(url, "", "width=582,height=700");
//console.log(newwin)
var open = true;
@@ -447,9 +523,7 @@ const AuthenticationOauth2 = (props) => {
] = "false";
} else {
alert.info(
- "Field " +
- selectedApp.authentication.parameters[key].name +
- " can't be empty"
+ "Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty"
);
return;
}
diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js
new file mode 100755
index 00000000..65dd6e97
--- /dev/null
+++ b/frontend/src/components/OrgHeader.js
@@ -0,0 +1,432 @@
+import React, { useEffect} from 'react';
+import { makeStyles } from '@material-ui/styles';
+import { useTheme } from '@material-ui/core/styles';
+
+import Tooltip from '@material-ui/core/Tooltip';
+import Grid from '@material-ui/core/Grid';
+import Button from '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+import Typography from '@material-ui/core/Typography';
+import { useAlert } from "react-alert";
+import IconButton from '@material-ui/core/IconButton';
+import ExpandLessIcon from '@material-ui/icons/ExpandLess';
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import SaveIcon from '@material-ui/icons/Save';
+
+const useStyles = makeStyles({
+ notchedOutline: {
+ borderColor: "#f85a3e !important"
+ },
+})
+
+const defaultImage = "data:image/png;base64,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"
+
+const OrgHeader = (props) => {
+ const {userdata, selectedOrganization, setSelectedOrganization, globalUrl} = props
+
+ const theme = useTheme()
+ const alert = useAlert()
+ const classes = useStyles()
+
+ var upload = ""
+ const defaultBranch = "master"
+ const [orgName, setOrgName] = React.useState(selectedOrganization.name)
+ const [orgDescription, setOrgDescription] = React.useState(selectedOrganization.description)
+ const [appDownloadUrl, setAppDownloadUrl] = React.useState(selectedOrganization.defaults === undefined ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.app_download_repo === undefined || selectedOrganization.defaults.app_download_repo.length === 0 ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.app_download_repo)
+ const [appDownloadBranch, setAppDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.app_download_branch === undefined || selectedOrganization.defaults.app_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.app_download_branch)
+ const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState(selectedOrganization.defaults === undefined ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.workflow_download_repo === undefined || selectedOrganization.defaults.workflow_download_repo.length === 0 ? "https://github.com/frikky/shuffle-workflows" : selectedOrganization.defaults.workflow_download_repo)
+ const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.workflow_download_branch === undefined || selectedOrganization.defaults.workflow_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.workflow_download_branch)
+ const [ssoEntrypoint, setSsoEntrypoint] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_entrypoint === undefined || selectedOrganization.sso_config.sso_entrypoint.length === 0 ? "" : selectedOrganization.sso_config.sso_entrypoint)
+ const [ssoCertificate, setSsoCertificate] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_certificate === undefined || selectedOrganization.sso_config.sso_certificate.length === 0 ? "" : selectedOrganization.sso_config.sso_certificate)
+
+ const [file, setFile] = React.useState("")
+ const [fileBase64, setFileBase64] = React.useState(selectedOrganization.image)
+ const [expanded, setExpanded] = React.useState(false)
+
+ if (file !== "") {
+ const img = document.getElementById('logo')
+ var canvas = document.createElement('canvas')
+ canvas.width = 174
+ canvas.height = 174
+ var ctx = canvas.getContext('2d')
+
+ img.onload = function() {
+ // img, x, y, width, height
+ //ctx.drawImage(img, 174, 174)
+ //console.log("IMG natural: ", img.naturalWidth, img.naturalHeight)
+ //ctx.drawImage(img, 0, 0, 174, 174)
+ ctx.drawImage(img,
+ 0, 0, img.width, img.height,
+ 0, 0, canvas.width, canvas.height
+ )
+
+ const canvasUrl = canvas.toDataURL()
+ if (canvasUrl !== fileBase64) {
+ setFileBase64(canvasUrl)
+ selectedOrganization.image = canvasUrl
+ setSelectedOrganization(selectedOrganization)
+ }
+ }
+ }
+
+ const handleEditOrg = (name, description, orgId, image, defaults, sso_config) => {
+ const data = {
+ "name": name,
+ "description": description,
+ "org_id": orgId,
+ "image": image,
+ "defaults": defaults,
+ "sso_config": sso_config,
+ }
+
+ const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
+ fetch(url, {
+ mode: 'cors',
+ method: 'POST',
+ body: JSON.stringify(data),
+ credentials: 'include',
+ crossDomain: true,
+ withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ })
+ .then(response =>
+ response.json().then(responseJson => {
+ if (responseJson["success"] === false) {
+ alert.error("Failed updating org: ", responseJson.reason)
+ } else {
+ alert.success("Successfully edited org!")
+ }
+ }),
+ )
+ .catch(error => {
+ alert.error("Err: " + error.toString())
+ });
+ }
+
+ var image = ""
+ const editHeaderImage = (event) => {
+ const file = event.target.value
+ const actualFile = event.target.files[0]
+ const fileObject = URL.createObjectURL(actualFile)
+ setFile(fileObject)
+ }
+
+ console.log("USER: ", userdata)
+ const orgSaveButton =
+
+ return (
+