diff --git a/backend/database/opensearch/docker-compose.yml b/backend/database/opensearch/docker-compose.yml new file mode 100644 index 00000000..9dfae6a1 --- /dev/null +++ b/backend/database/opensearch/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3' +services: + opensearch-node1: + image: opensearchproject/opensearch:latest + hostname: shuffle-database + container_name: shuffle-opensearch + environment: + - cluster.name=shuffle-cluster + - node.name=shuffle-opensearch + - discovery.seed_hosts=shuffle-opensearch + - cluster.initial_master_nodes=shuffle-opensearch + - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + - cluster.routing.allocation.disk.threshold_enabled=false + - opendistro_security.disabled=true + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems + hard: 65536 + volumes: + - ~/git/shuffle/shuffle-database:/usr/share/opensearch/data + ports: + - 9200:9200 + networks: + - opensearch-net + +volumes: + opensearch-data1: + +networks: + opensearch-net: diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 2e9a4314..62c5878b 100644 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -2,7 +2,7 @@ package main // Docker import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "archive/tar" //"bufio" @@ -778,7 +778,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { newClient, err := newdockerclient.NewClientFromEnv() if err != nil { - log.Printf("[WARNING] Failed setting up docker env: %s", newClient) + log.Printf("[ERROR] Failed setting up docker env: %#v", newClient) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't make docker client"}`))) return @@ -792,7 +792,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { } if err := newClient.ExportImage(opts); err != nil { - log.Printf("[WARNING] FAILED to save image to file: %s", err) + log.Printf("[ERROR] FAILED to save image to file: %s", err) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't export image"}`))) return diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 7dc5e24a..16f22e0f 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.15 // indirect + github.com/frikky/shuffle-shared v0.1.15 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 @@ -36,7 +36,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.1.14 + github.com/shuffle/shuffle-shared v0.1.15 go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index e330ca0f..2b644674 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "bufio" "bytes" @@ -580,23 +580,6 @@ func redirect(w http.ResponseWriter, req *http.Request) { http.StatusTemporaryRedirect) } -func parseLoginParameters(resp http.ResponseWriter, request *http.Request) (loginStruct, error) { - - body, err := ioutil.ReadAll(request.Body) - if err != nil { - return loginStruct{}, err - } - - var t loginStruct - - err = json.Unmarshal(body, &t) - if err != nil { - return loginStruct{}, err - } - - return t, nil -} - // No more emails :) func checkUsername(Username string) error { // Stupid first check of email loool @@ -731,6 +714,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { // Only admin can CREATE users, but if there are no users, anyone can make (first) ctx := context.Background() users, countErr := shuffle.GetAllUsers(ctx) + count := len(users) user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { @@ -753,7 +737,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } // Gets a struct of Username, password - data, err := parseLoginParameters(resp, request) + data, err := shuffle.ParseLoginParameters(resp, request) if err != nil { log.Printf("Invalid params: %s", err) resp.WriteHeader(401) @@ -813,7 +797,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { err = shuffle.SetEnvironment(ctx, &item) if err != nil { - log.Printf("[WARNING] Failed setting up new environment for new org: %s") + log.Printf("[WARNING] Failed setting up new environment for new org: %s", err) } currentOrg = shuffle.OrgMini{ @@ -1194,7 +1178,7 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) { } // Gets a struct of Username, password - data, err := parseLoginParameters(resp, request) + data, err := shuffle.ParseLoginParameters(resp, request) if err != nil { resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) @@ -1971,6 +1955,10 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { // 1. Get callback data // 2. Load the configuration // 3. Execute the workflow + cors := shuffle.HandleCors(resp, request) + if cors { + return + } path := strings.Split(request.URL.String(), "/") if len(path) < 4 { @@ -5173,7 +5161,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { // 2. If cloud env found, enable it (un-archive) // 3. If it doesn't create it environments, err := shuffle.GetEnvironments(ctx, org.Id) - log.Printf("GETTING ENVS: %#s", environments) + log.Printf("GETTING ENVS: %#v", environments) if err == nil { // Don't disable, this will be deleted entirely diff --git a/backend/go-app/main_test.go b/backend/go-app/main_test.go new file mode 100644 index 00000000..e9d358b6 --- /dev/null +++ b/backend/go-app/main_test.go @@ -0,0 +1,340 @@ +package main + +import ( + "github.com/shuffle/shuffle-shared" + + "bytes" + "context" + "log" + "net/http" + "net/http/httptest" + "reflect" + "runtime" + "testing" + "time" + + "cloud.google.com/go/datastore" + "cloud.google.com/go/storage" + "google.golang.org/api/option" + + "google.golang.org/grpc" +) + +type endpoint struct { + handler http.HandlerFunc + path string + method string +} + +func init() { + ctx := context.Background() + dbclient, err := datastore.NewClient(ctx, gceProject, option.WithGRPCDialOption(grpc.WithNoProxy())) + if err != nil { + log.Fatalf("[DEBUG] Database client error during init: %s", err) + } + + _, err = shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", true, "elasticsearch") + log.Printf("INIT") +} + +// TestTestAuthenticationRequired tests that the handlers in the `handlers` +// variable returns 401 Unauthorized when called without credentials. +func TestAuthenticationRequired(t *testing.T) { + handlers := []endpoint{ + {handler: shuffle.HandleNewOutlookRegister, path: "/functions/outlook/register", method: "GET"}, + {handler: shuffle.HandleGetOutlookFolders, path: "/functions/outlook/getFolders", method: "GET"}, + {handler: shuffle.HandleApiGeneration, path: "/api/v1/users/generateapikey", method: "GET"}, + {handler: handleLogin, path: "/api/v1/users/login", method: "POST"}, // prob not this one + // handleRegister generates nil pointer exception. Not necessary for this anyway. + //{handler: handleRegister, path: "/api/v1/users/register", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users/getusers", method: "GET"}, + {handler: handleInfo, path: "/api/v1/users/getinfo", method: "GET"}, + {handler: shuffle.HandleSettings, path: "/api/v1/users/getsettings", method: "GET"}, + {handler: shuffle.HandleUpdateUser, path: "/api/v1/users/updateuser", method: "PUT"}, + {handler: shuffle.DeleteUser, path: "/api/v1/users/123", method: "DELETE"}, + {handler: shuffle.HandlePasswordChange, path: "/api/v1/users/passwordchange", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users", method: "GET"}, + {handler: shuffle.HandleGetEnvironments, path: "/api/v1/getenvironments", method: "GET"}, + {handler: shuffle.HandleSetEnvironments, path: "/api/v1/setenvironments", method: "PUT"}, + + // handleWorkflowQueue generates nil pointer exception + //{handler: handleWorkflowQueue, path: "/api/v1/streams", method: "POST"}, + // handleGetStreamResults generates nil pointer exception + //{handler: handleGetStreamResults, path: "/api/v1/streams/results", method: "POST"}, + + {handler: handleAppHotloadRequest, path: "/api/v1/apps/run_hotload", method: "GET"}, + {handler: LoadSpecificApps, path: "/api/v1/apps/get_existing", method: "POST"}, + {handler: shuffle.UpdateWorkflowAppConfig, path: "/api/v1/apps/123", method: "PATCH"}, + {handler: validateAppInput, path: "/api/v1/apps/validate", method: "POST"}, + {handler: shuffle.DeleteWorkflowApp, path: "/api/v1/apps/123", method: "DELETE"}, + {handler: shuffle.GetWorkflowAppConfig, path: "/api/v1/apps/123/config", method: "GET"}, + {handler: getWorkflowApps, path: "/api/v1/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/apps", method: "PUT"}, + //{handler: shuffle.GetSpecificApps, path: "/api/v1/apps/search", method: "POST"}, + + {handler: shuffle.GetAppAuthentication, path: "/api/v1/apps/authentication", method: "GET"}, + {handler: shuffle.AddAppAuthentication, path: "/api/v1/apps/authentication", method: "PUT"}, + {handler: shuffle.DeleteAppAuthentication, path: "/api/v1/apps/authentication/123", method: "DELETE"}, + + {handler: validateAppInput, path: "/api/v1/workflows/apps/validate", method: "POST"}, + {handler: getWorkflowApps, path: "/api/v1/workflows/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/workflows/apps", method: "PUT"}, + + {handler: shuffle.GetWorkflows, path: "/api/v1/workflows", method: "GET"}, + {handler: shuffle.SetNewWorkflow, path: "/api/v1/workflows", method: "POST"}, + {handler: handleGetWorkflowqueue, path: "/api/v1/workflows/queue", method: "GET"}, + {handler: handleGetWorkflowqueueConfirm, path: "/api/v1/workflows/queue/confirm", method: "POST"}, + {handler: shuffle.HandleGetSchedules, path: "/api/v1/workflows/schedules", method: "GET"}, + {handler: loadSpecificWorkflows, path: "/api/v1/workflows/download_remote", method: "POST"}, + {handler: executeWorkflow, path: "/api/v1/workflows/123/execute", method: "GET"}, + {handler: scheduleWorkflow, path: "/api/v1/workflows/123/schedule", method: "POST"}, + {handler: stopSchedule, path: "/api/v1/workflows/123/schedule/abc", method: "DELETE"}, + // createOutlookSub generates nil pointer exception + {handler: shuffle.HandleCreateOutlookSub, path: "/api/v1/workflows/123/outlook", method: "POST"}, + // handleDeleteOutlookSub generates nil pointer exception + {handler: shuffle.HandleDeleteOutlookSub, path: "/api/v1/workflows/123/outlook/abc", method: "DELETE"}, + {handler: shuffle.GetWorkflowExecutions, path: "/api/v1/workflows/123/executions", method: "GET"}, + {handler: shuffle.AbortExecution, path: "/api/v1/workflows/123/executions/abc/abort", method: "GET"}, + {handler: shuffle.GetSpecificWorkflow, path: "/api/v1/workflows/123", method: "GET"}, + {handler: shuffle.SaveWorkflow, path: "/api/v1/workflows/123", method: "PUT"}, + {handler: deleteWorkflow, path: "/api/v1/workflows/123", method: "DELETE"}, + + {handler: shuffle.HandleNewHook, path: "/api/v1/hooks/new", method: "POST"}, + {handler: handleWebhookCallback, path: "/api/v1/hooks/123", method: "POST"}, + {handler: shuffle.HandleDeleteHook, path: "/api/v1/hooks/123/delete", method: "DELETE"}, + + {handler: shuffle.HandleGetSpecificTrigger, path: "/api/v1/triggers/123", method: "GET"}, + //{handler: shuffle.HandleGetSpecificStats, path: "/api/v1/stats/123", method: "GET"}, + + {handler: verifySwagger, path: "/api/v1/verify_swagger", method: "POST"}, + {handler: verifySwagger, path: "/api/v1/verify_openapi", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/get_openapi_uri", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: shuffle.ValidateSwagger, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: getOpenapi, path: "/api/v1/get_openapi", method: "GET"}, + + //{handler: shuffle.CleanupExecutions, path: "/api/v1/execution_cleanup", method: "GET"}, + + {handler: handleCloudSetup, path: "/api/v1/cloud/setup", method: "POST"}, + {handler: shuffle.HandleGetOrgs, path: "/api/v1/orgs", method: "POST"}, + {handler: shuffle.HandleGetFileContent, path: "/api/v1/files/{fileId}/content", method: "POST"}, + } + + var err error + ctx := context.Background() + + // Most handlers requires database access in order to not crash or cause + // nil pointer issues. + // To start a local database instance, run: + // docker-compose up database + // To let the tests know about the database, run: + // DATASTORE_EMULATOR_HOST=0.0.0.0:8000 go test + dbclient, err = datastore.NewClient(ctx, gceProject, option.WithGRPCDialOption(grpc.WithNoProxy())) + if err != nil { + t.Fatal(err) + } + + dummyBody := bytes.NewBufferString("dummy") + + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest(e.method, e.path, dummyBody) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(e.handler) + + timeoutHandler := http.TimeoutHandler(handler, 2*time.Second, `Request Timeout.`) + timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusUnauthorized { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusUnauthorized) + } + } +} + +func TestAuthenticationNotRequired(t *testing.T) { + // All of these return 200 OK when user not logged in + handlers := []endpoint{ + {handler: checkAdminLogin, path: "/api/v1/users/checkusers", method: "GET"}, + {handler: shuffle.HandleLogout, path: "/api/v1/users/logout", method: "POST"}, + {handler: shuffle.GetDocList, path: "/api/v1/docs", method: "GET"}, + {handler: shuffle.GetDocs, path: "/api/v1/docs/123", method: "GET"}, + {handler: healthCheckHandler, path: "/api/v1/_ah/health"}, + } + + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest(e.method, e.path, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(e.handler) + + timeoutHandler := http.TimeoutHandler(handler, 2*time.Second, `Request Timeout.`) + timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusOK { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusOK) + } + } +} + +// TestCors tests that all endpoints returns the same CORS headers when hit +// with an OPTIONS type request. +// It feels very fragile to test headers like this, especially for the +// "Access-Control-Allow-Origin", but this test should be helpful while +// refactoring the CORS logic into a middleware, and that's the reason this +// test exists right now. It might change after the refactor because our +// requirements might change after the refactor. +func TestCors(t *testing.T) { + handlers := []endpoint{ + {handler: shuffle.HandleNewOutlookRegister, path: "/functions/outlook/register", method: "GET"}, + {handler: shuffle.HandleGetOutlookFolders, path: "/functions/outlook/getFolders", method: "GET"}, + {handler: shuffle.HandleApiGeneration, path: "/api/v1/users/generateapikey", method: "GET"}, + {handler: handleLogin, path: "/api/v1/users/login", method: "POST"}, // prob not this one + // handleRegister generates nil pointer exception + {handler: handleRegister, path: "/api/v1/users/register", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users/getusers", method: "GET"}, + {handler: handleInfo, path: "/api/v1/users/getinfo", method: "GET"}, + {handler: shuffle.HandleSettings, path: "/api/v1/users/getsettings", method: "GET"}, + {handler: shuffle.HandleUpdateUser, path: "/api/v1/users/updateuser", method: "PUT"}, + {handler: shuffle.DeleteUser, path: "/api/v1/users/123", method: "DELETE"}, + // handlePasswordChange generates nil pointer exception + {handler: shuffle.HandlePasswordChange, path: "/api/v1/users/passwordchange", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users", method: "GET"}, + {handler: shuffle.HandleGetEnvironments, path: "/api/v1/getenvironments", method: "GET"}, + {handler: shuffle.HandleSetEnvironments, path: "/api/v1/setenvironments", method: "PUT"}, + + // handleWorkflowQueue generates nil pointer exception + {handler: handleWorkflowQueue, path: "/api/v1/streams", method: "POST"}, + // handleGetStreamResults generates nil pointer exception + {handler: handleGetStreamResults, path: "/api/v1/streams/results", method: "POST"}, + + {handler: handleAppHotloadRequest, path: "/api/v1/apps/run_hotload", method: "GET"}, + {handler: LoadSpecificApps, path: "/api/v1/apps/get_existing", method: "POST"}, + {handler: shuffle.UpdateWorkflowAppConfig, path: "/api/v1/apps/123", method: "PATCH"}, + {handler: validateAppInput, path: "/api/v1/apps/validate", method: "POST"}, + {handler: shuffle.DeleteWorkflowApp, path: "/api/v1/apps/123", method: "DELETE"}, + {handler: shuffle.GetWorkflowAppConfig, path: "/api/v1/apps/123/config", method: "GET"}, + {handler: getWorkflowApps, path: "/api/v1/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/apps", method: "PUT"}, + //{handler: shuffle.GetSpecificApps, path: "/api/v1/apps/search", method: "POST"}, + + {handler: shuffle.GetAppAuthentication, path: "/api/v1/apps/authentication", method: "GET"}, + {handler: shuffle.AddAppAuthentication, path: "/api/v1/apps/authentication", method: "PUT"}, + {handler: shuffle.DeleteAppAuthentication, path: "/api/v1/apps/authentication/123", method: "DELETE"}, + + {handler: validateAppInput, path: "/api/v1/workflows/apps/validate", method: "POST"}, + {handler: getWorkflowApps, path: "/api/v1/workflows/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/workflows/apps", method: "PUT"}, + + {handler: shuffle.GetWorkflows, path: "/api/v1/workflows", method: "GET"}, + {handler: shuffle.SetNewWorkflow, path: "/api/v1/workflows", method: "POST"}, + {handler: handleGetWorkflowqueue, path: "/api/v1/workflows/queue", method: "GET"}, + {handler: handleGetWorkflowqueueConfirm, path: "/api/v1/workflows/queue/confirm", method: "POST"}, + {handler: shuffle.HandleGetSchedules, path: "/api/v1/workflows/schedules", method: "GET"}, + {handler: loadSpecificWorkflows, path: "/api/v1/workflows/download_remote", method: "POST"}, + {handler: executeWorkflow, path: "/api/v1/workflows/123/execute", method: "GET"}, + {handler: scheduleWorkflow, path: "/api/v1/workflows/123/schedule", method: "POST"}, + {handler: stopSchedule, path: "/api/v1/workflows/123/schedule/abc", method: "DELETE"}, + // createOutlookSub generates nil pointer exception + {handler: shuffle.HandleCreateOutlookSub, path: "/api/v1/workflows/123/outlook", method: "POST"}, + // handleDeleteOutlookSub generates nil pointer exception + {handler: shuffle.HandleDeleteOutlookSub, path: "/api/v1/workflows/123/outlook/abc", method: "DELETE"}, + {handler: shuffle.GetWorkflowExecutions, path: "/api/v1/workflows/123/executions", method: "GET"}, + {handler: shuffle.AbortExecution, path: "/api/v1/workflows/123/executions/abc/abort", method: "GET"}, + {handler: shuffle.GetSpecificWorkflow, path: "/api/v1/workflows/123", method: "GET"}, + {handler: shuffle.SaveWorkflow, path: "/api/v1/workflows/123", method: "PUT"}, + {handler: deleteWorkflow, path: "/api/v1/workflows/123", method: "DELETE"}, + + {handler: shuffle.HandleNewHook, path: "/api/v1/hooks/new", method: "POST"}, + {handler: handleWebhookCallback, path: "/api/v1/hooks/123", method: "POST"}, + {handler: shuffle.HandleDeleteHook, path: "/api/v1/hooks/123/delete", method: "DELETE"}, + + {handler: shuffle.HandleGetSpecificTrigger, path: "/api/v1/triggers/123", method: "GET"}, + //{handler: shuffle.HandleGetSpecificStats, path: "/api/v1/stats/123", method: "GET"}, + + {handler: verifySwagger, path: "/api/v1/verify_swagger", method: "POST"}, + {handler: verifySwagger, path: "/api/v1/verify_openapi", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/get_openapi_uri", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: shuffle.ValidateSwagger, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: getOpenapi, path: "/api/v1/get_openapi", method: "GET"}, + + //{handler: shuffle.CleanupExecutions, path: "/api/v1/execution_cleanup", method: "GET"}, + + {handler: handleCloudSetup, path: "/api/v1/cloud/setup", method: "POST"}, + {handler: shuffle.HandleGetOrgs, path: "/api/v1/orgs", method: "POST"}, + {handler: shuffle.HandleGetFileContent, path: "/api/v1/files/{fileId}/content", method: "POST"}, + } + + //r := initHandlers(context.TODO()) + initHandlers() + +outerLoop: + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest("OPTIONS", e.path, nil) + req.Header.Add("Origin", "http://localhost:3000") + req.Header.Add("Access-Control-Request-Method", "POST") + req.Header.Add("Access-Control-Request-Headers", "Content-Type, Accept, X-Requested-With, remember-me") + + // OPTIONS /resource/foo + // Access-Control-Request-Method: DELETE + // Access-Control-Request-Headers: origin, x-requested-with + // Origin: https://foo.bar.org + + if err != nil { + t.Errorf("Failure in OPTIONS setup: %s", err) + continue + } + + rr := httptest.NewRecorder() + + //timeoutHandler := http.TimeoutHandler(r, 2*time.Second, `Request Timeout`) + //timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusOK { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusOK) + continue + } + + want := map[string]string{ + "Vary": "Origin", + "Access-Control-Allow-Headers": "Content-Type, Accept, X-Requested-With, Remember-Me", + "Access-Control-Allow-Methods": "POST", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Origin": "http://localhost:3000", + } + + // Remember to use canonical header name if accessing the headers array + // directly: + // v := r.Header[textproto.CanonicalMIMEHeaderKey("foo")] + // When using Header().Get(h), h will automatically be converted to canonical format. + + for key, value := range want { + got := rr.Header().Get(key) + if got != value { + t.Errorf("%s handler returned wrong value for '%s' header: got '%v' want '%v'", + funcName, key, got, value) + continue outerLoop + } + } + + } +} + +func getFunctionNameFromFunction(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} diff --git a/backend/go-app/run.sh b/backend/go-app/run.sh new file mode 100755 index 00000000..2ebf72c9 --- /dev/null +++ b/backend/go-app/run.sh @@ -0,0 +1 @@ +go run main.go walkoff.go docker.go diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index a4eb0bca..7078110e 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1,7 +1,7 @@ package main import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "bytes" "context" @@ -709,6 +709,11 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { return } + if request.Body == nil { + resp.WriteHeader(http.StatusBadRequest) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { log.Println("Failed reading body for stream result queue") @@ -762,9 +767,14 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { return } + if request.Body == nil { + resp.WriteHeader(http.StatusBadRequest) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { - log.Println("(3) Failed reading body for workflowqueue") + log.Println("[WARNING] (3) Failed reading body for workflowqueue") resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) return @@ -2636,25 +2646,18 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) { return } - // FIXME - set this to be per user IF logged in, - // as there might exist private and public - //memcacheName := "all_apps" - ctx := context.Background() - // Just need to be logged in - // FIXME - need to be logged in? user, userErr := shuffle.HandleApiAuthentication(resp, request) if userErr != nil { - log.Printf("Continuing with apps even without auth") - //log.Printf("Api authentication failed in get all apps: %s", userErr) - //resp.WriteHeader(401) - //resp.Write([]byte(`{"success": false}`)) - //return + log.Printf("[WARNING] Api authentication failed in get all apps - this does NOT require auth in cloud.: %s", userErr) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return } workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000) if err != nil { - log.Printf("Failed getting apps (getworkflowapps): %s", err) + log.Printf("{WARNING] Failed getting apps (getworkflowapps): %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return