diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 253bf8e2..18a42a32 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -824,6 +824,26 @@ class AppBase: return True, "" + # Things to consider for files: + # - How can you download / stream a file? + # - Can you decide if you want a stream or the files directly? + def get_files(full_execution, value): + print("FULL EXEC: %s" % full_execution) + org_id = full_execution["workflow"]["execution_org"]["id"] + print("SHOULD GET FILES BASED ON ORG %s, workflow %s and value(s) %s" % (org_id, full_execution["workflow"]["id"], value)) + get_path = "/api/v1/files/%s/content?execution_id=%s" % (value, full_execution["execution_id"]) + + print("PATH: %s" % get_path) + headers = { + "Content-Type": "application/json", + "Authorization": "Bearer %s" % self.authorization + } + ret = requests.get("%s%s" % (self.url, get_path), headers=headers) + print("RET CONTENT: %s" % ret.text) + print("RET CODE FILE: %d" % ret.status_code) + + # r.HandleFunc("/api/v1/files/{fileId}/content", handleGetFileContent).Methods("GET", "OPTIONS") + # Checks whether conditions are met, otherwise set branchcheck, tmpresult = check_branch_conditions(action, fullexecution) if not branchcheck: @@ -848,7 +868,7 @@ class AppBase: actionname.replace(" ", "_", -1) #if action.generated: # actionname = actionname.lower() - + # Runs the actual functions try: func = getattr(self, actionname, None) @@ -891,8 +911,16 @@ class AppBase: multiexecution = False multi_execution_lists = [] for parameter in action["parameters"]: - check, value, is_loop = parse_params(action, fullexecution, parameter) + is_file = False + try: + if parameter["schema"]["type"] == "file": + print("SHOULD HANDLE FILE. Get based on value %s" % parameter["value"]) + get_files(fullexecution, parameter["value"]) + is_file = True + except KeyError as e: + print("SCHEMA ERROR: %s" % e) + check, value, is_loop = parse_params(action, fullexecution, parameter) if check: raise "Value check error: %s" % Exception(check) diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 5e2fc3aa..6c28cc03 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,11 +3,11 @@ NAME=shuffle-app_sdk VERSION=0.8.0 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force -docker build . -t frikky/shuffle:$NAME -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION +docker build . -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION #docker push frikky/$NAME:$VERSION #docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION #docker push ghcr.io/frikky/$NAME:$VERSION -docker push frikky/shuffle:$NAME +docker push frikky/shuffle:app_sdk docker push ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ff2685c4..d7fe77a6 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -620,9 +620,6 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U return User{}, errors.New("Invalid format for apikey") } - // fml - //log.Println(apikeyCheck) - // This is annoying af and is done because of maxlength lol newApikey := apikeyCheck[1] if len(newApikey) > 249 { @@ -630,57 +627,15 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U } ctx := context.Background() - //if item, err := memcache.Get(ctx, newApikey); err == memcache.ErrCacheMiss { - // // Not in cache - //} else if err != nil { - // // Error with cache - // log.Printf("Error getting item: %v", err) - //} else { - // var Userdata User - // err = json.Unmarshal(item.Value, &Userdata) - - // if err == nil { - // if len(Userdata.Username) > 0 { - // return Userdata, nil - // } else { - // return Userdata, errors.New("User is invalid") - // } - // } - //} // Make specific check for just service user? // Get the user based on APIkey here - //log.Println(apikeyCheck[1]) Userdata, err := getApikey(ctx, apikeyCheck[1]) if err != nil { log.Printf("Apikey %s doesn't exist: %s", apikey, err) return User{}, err } - // Caching both bad and good apikeys :) - //b, err := json.Marshal(Userdata) - //if err != nil { - // log.Printf("Failed marshalling: %s", err) - // return User{}, err - //} - - // Add to cache if it doesn't exist - //item := &memcache.Item{ - // Key: newApikey, - // Value: b, - // Expiration: time.Minute * 60, - //} - - //if err := memcache.Add(ctx, item); err == memcache.ErrNotStored { - // if err := memcache.Set(ctx, item); err != nil { - // log.Printf("Error setting item: %v", err) - // } - //} else if err != nil { - // log.Printf("error adding item: %v", err) - //} else { - // log.Printf("Set cache for %s", item.Key) - //} - if len(Userdata.Username) > 0 { return Userdata, nil } else { @@ -3957,7 +3912,7 @@ func executeSchedule(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() - log.Printf("EXECUTING %s!", workflowId) + log.Printf("[INFO] EXECUTING %s!", workflowId) idConfig, err := getSchedule(ctx, workflowId) if err != nil { log.Printf("Error getting schedule: %s", err) @@ -7186,39 +7141,37 @@ func runInit(ctx context.Context) { } } - /* - fileq := datastore.NewQuery("Files").Limit(1) - count, err := dbclient.Count(ctx, fileq) + fileq := datastore.NewQuery("Files").Limit(1) + count, err := dbclient.Count(ctx, fileq) + log.Printf("FILECOUNT: %d", count) + if err == nil && count < 10 { + basepath := "." + filename := "testfile.txt" + fileId := uuid.NewV4().String() + log.Printf("Creating new file reference %s because none exist!", fileId) + workflowId := "2cf1169d-b460-41de-8c36-28b2092866f8" + downloadPath := fmt.Sprintf("%s/%s/%s/%s", basepath, activeOrgs[0].Id, workflowId, fileId) - if err == nil && count == 0 { - basepath := "." - filename := "testfile.txt" - fileId := uuid.NewV4().String() - log.Printf("Creating new file reference %s because none exist!", fileId) - workflowId := "2e9d6474-402c-4dcc-bb53-45f638ca18d3" - downloadPath := fmt.Sprintf("%s/%s/%s/%s", basepath, activeOrgs[0].Id, workflowId, fileId) - - timeNow := time.Now().Unix() - newFile := File{ - Id: fileId, - CreatedAt: timeNow, - UpdatedAt: timeNow, - Description: "Created by system for testing", - Status: "active", - Filename: filename, - OrgId: activeOrgs[0].Id, - WorkflowId: workflowId, - DownloadPath: downloadPath, - } - - err = setFile(ctx, newFile) - if err != nil { - log.Printf("Failed setting file: %s", err) - } else { - log.Printf("Created file %s in init", newFile.DownloadPath) - } + timeNow := time.Now().Unix() + newFile := File{ + Id: fileId, + CreatedAt: timeNow, + UpdatedAt: timeNow, + Description: "Created by system for testing", + Status: "active", + Filename: filename, + OrgId: activeOrgs[0].Id, + WorkflowId: workflowId, + DownloadPath: downloadPath, } - */ + + err = setFile(ctx, newFile) + if err != nil { + log.Printf("Failed setting file: %s", err) + } else { + log.Printf("Created file %s in init", newFile.DownloadPath) + } + } var allworkflowapps []AppAuthenticationStorage q = datastore.NewQuery("workflowappauth") @@ -7241,7 +7194,7 @@ func runInit(ctx context.Context) { var schedules []ScheduleOld q = datastore.NewQuery("schedules") - _, err := dbclient.GetAll(ctx, q, &schedules) + _, err = dbclient.GetAll(ctx, q, &schedules) if err == nil { log.Printf("Setting up all schedules with org %s", activeOrgs[0].Id) for _, item := range schedules { @@ -8016,13 +7969,59 @@ func handleGetFileContent(resp http.ResponseWriter, request *http.Request) { fileId = location[4] } - //log.Printf("In file download") + log.Printf("\n\nUser is trying to get file %s\n\n", fileId) + + // 1. Check user directly + // 2. Check workflow execution authorization + setOrgId := false user, err := handleApiAuthentication(resp, request) if err != nil { - log.Printf("Api authentication failed in file download: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return + // r.HandleFunc("/api/v1/files/{fileId}/content", handleGetFileContent).Methods("GET", "OPTIONS") + log.Printf("INITIAL Api authentication failed in file download: %s", err) + executionId, ok := request.URL.Query()["execution_id"] + if ok && len(executionId) > 0 { + ctx := context.Background() + workflowExecution, err := getWorkflowExecution(ctx, executionId[0]) + if err != nil { + log.Printf("Couldn't find execution ID %s", executionId[0]) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + apikey := request.Header.Get("Authorization") + if !strings.HasPrefix(apikey, "Bearer ") { + log.Printf("Apikey doesn't start with bearer (2)") + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + apikeyCheck := strings.Split(apikey, " ") + if len(apikeyCheck) != 2 { + log.Printf("Invalid format for apikey (2)") + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // This is annoying af and is done because of maxlength lol + newApikey := apikeyCheck[1] + if newApikey != workflowExecution.Authorization { + log.Printf("Bad apikey for execution %s. %s vs %s", executionId[0], apikey, workflowExecution.Authorization) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + log.Printf("Authorization is correct for execution %s! %s vs %s", executionId, apikey, workflowExecution.Authorization) + setOrgId = true + } else { + + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } } // 1. Verify if the user has access to the file: org_id and workflow @@ -8037,6 +8036,11 @@ func handleGetFileContent(resp http.ResponseWriter, request *http.Request) { return } + // This is a workaround for file grabs from an app + if setOrgId == true { + user.ActiveOrg.Id = file.OrgId + } + found := false if file.OrgId == user.ActiveOrg.Id { found = true diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 1bd98563..33c438f7 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1089,14 +1089,14 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { } } - log.Printf("Updating %s in %s from %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, workflowExecution.Results[outerindex].Status, actionResult.Status) + log.Printf("[INFO] Updating %s in %s from %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, workflowExecution.Results[outerindex].Status, actionResult.Status) workflowExecution.Results[outerindex] = actionResult } else { - log.Printf("Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status) + log.Printf("[INFO] Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status) workflowExecution.Results = append(workflowExecution.Results, actionResult) } } else { - log.Printf("Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status) + log.Printf("[INFO] Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status) workflowExecution.Results = append(workflowExecution.Results, actionResult) } @@ -1179,7 +1179,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { _ = skippedNodes if finished { - log.Printf("Execution of %s finished.", workflowExecution.ExecutionId) + log.Printf("[INFO] Execution of %s finished.", workflowExecution.ExecutionId) //log.Println("Might be finished based on length of results and everything being SUCCESS or FINISHED - VERIFY THIS. Setting status to finished.") workflowExecution.Result = lastResult @@ -1316,15 +1316,15 @@ func handleExecutionStatistics(execution WorkflowExecution) { for _, exampleresult := range appResults { err := setExampleresult(ctx, exampleresult) if err != nil { - log.Printf("Failed setting examplresult %s: %s", exampleresult.ExampleId, err) + log.Printf("[ERROR] Failed setting examplresult %s: %s", exampleresult.ExampleId, err) } else { successful += 1 } } - log.Printf("Added %d exampleresults to backend", successful) + log.Printf("[INFO] Added %d exampleresults to backend", successful) } else { - log.Printf("No example results necessary to be added for execution %s", execution.ExecutionId) + log.Printf("[INFO] No example results necessary to be added for execution %s", execution.ExecutionId) } } @@ -1438,6 +1438,7 @@ func setNewWorkflow(resp http.ResponseWriter, request *http.Request) { workflow.ID = uuid.NewV4().String() workflow.Owner = user.Id workflow.Sharing = "private" + user.ActiveOrg.Users = []User{} workflow.ExecutingOrg = user.ActiveOrg workflow.OrgId = user.ActiveOrg.Id @@ -1816,7 +1817,8 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) { } if len(workflow.ExecutingOrg.Id) == 0 { - log.Printf("setting executing org") + log.Printf("Setting executing org for workflow") + user.ActiveOrg.Users = []User{} workflow.ExecutingOrg = user.ActiveOrg } @@ -2339,17 +2341,17 @@ func abortExecution(resp http.ResponseWriter, request *http.Request) { // FIXME - have a check for org etc too.. if user.Id != workflowExecution.Workflow.Owner && user.Role != "admin" { - log.Printf("Wrong user (%s) for workflowexecution workflow %s", user.Username, workflowExecution.Workflow.ID) + log.Printf("[INFO] Wrong user (%s) for workflowexecution workflow %s", user.Username, workflowExecution.Workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return } } else { - log.Printf("API key to abort/finish execution %s is correct.", executionId) + log.Printf("[INFO] API key to abort/finish execution %s is correct.", executionId) } if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" || workflowExecution.Status == "FINISHED" { - log.Printf("Stopped execution of %s with status %s", executionId, workflowExecution.Status) + log.Printf("[INFO] Stopped execution of %s with status %s", executionId, workflowExecution.Status) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Status for %s is %s, which can't be aborted."}`, executionId, workflowExecution.Status))) return @@ -2461,7 +2463,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf } if len(workflow.ExecutingOrg.Id) == 0 { - log.Printf("Stopped execution because there is no executing org for workflow %s", workflow.ID) + log.Printf("[INFO] Stopped execution because there is no executing org for workflow %s", workflow.ID) return WorkflowExecution{}, fmt.Sprintf("Workflow has no executing org defined"), errors.New("Workflow has no executing org defined") } @@ -2479,7 +2481,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf } if !workflow.IsValid { - log.Printf("Stopped execution as workflow %s is not valid.", workflow.ID) + log.Printf("[ERROR] Stopped execution as workflow %s is not valid.", workflow.ID) return WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow") } @@ -3104,6 +3106,8 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) { } log.Printf("[INFO] Starting execution of %s!", fileId) + + user.ActiveOrg.Users = []User{} workflow.ExecutingOrg = user.ActiveOrg workflowExecution, executionResp, err := handleExecution(fileId, *workflow, request) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 033efc20..f7bf156d 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2836,7 +2836,16 @@ const AngularWorkflow = (props) => { }} /> - if (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0) { + console.log(selectedActionParameters[count]) + if (selectedActionParameters[count].schema !== undefined && selectedActionParameters[count].schema !== null && selectedActionParameters[count].schema.type === "file") { + const fileId = "6daabec1-892b-469c-b603-c902e47223a9" + datafield = `SHOW FILES FROM OTHER NODES? Filename: ${selectedActionParameters[count].value}` + if (selectedActionParameters[count].value != fileId) { + changeActionParameter(fileId, count) + setUpdate(Math.random()) + + } + } else if (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0) { if (selectedActionParameters[count].value === "" && selectedActionParameters[count].required) { // Rofl, dirty workaround :) const e = {