Fixed webhooks for local execution

This commit is contained in:
frikky
2020-05-12 12:35:05 +02:00
parent 8775fd2c21
commit cf0d365eaf
9 changed files with 267 additions and 226 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Shuffle # Shuffle
[Shuffler](https://shuffler.io) is an automation platform for your security stack. It leverages docker for scaling and OpenAPI for integrations. It has the possibility to run across multiple isolated environments, and gives you powerful tools to track progress. [Shuffle](https://shuffler.io) is an automation platform for your security stack. It leverages docker for scaling and OpenAPI for integrations. It has the possibility to run across multiple isolated environments, and gives you powerful tools to track progress.
## Try it ## Try it
Check out the [installation guide](https://github.com/frikky/shuffle/blob/master/install-guide.md) Check out the [installation guide](https://github.com/frikky/shuffle/blob/master/install-guide.md)
+1
View File
@@ -7,6 +7,7 @@ ADD ./go-app/main.go /app
ADD ./go-app/walkoff.go /app ADD ./go-app/walkoff.go /app
ADD ./go-app/docker.go /app ADD ./go-app/docker.go /app
ADD ./go-app/codegen.go /app ADD ./go-app/codegen.go /app
ADD ./go-app/webhook.go /app
ADD ./go-app/go.mod /app ADD ./go-app/go.mod /app
+1
View File
@@ -17,6 +17,7 @@ require (
github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.0.0 github.com/go-git/go-git/v5 v5.0.0
github.com/google/go-github/v28 v28.1.1 github.com/google/go-github/v28 v28.1.1
github.com/gorilla/handlers v1.4.2 // indirect
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.7.4
github.com/h2non/filetype v1.0.12 github.com/h2non/filetype v1.0.12
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
+2
View File
@@ -125,6 +125,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao= github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
+113 -56
View File
@@ -316,13 +316,14 @@ type HookAction struct {
} }
type Hook struct { type Hook struct {
Id string `json:"id" datastore:"id"` Id string `json:"id" datastore:"id"`
Info Info `json:"info" datastore:"info"` Info Info `json:"info" datastore:"info"`
Actions []HookAction `json:"actions" datastore:"actions"` Actions []HookAction `json:"actions" datastore:"actions"`
Type string `json:"type" datastore:"type"` Type string `json:"type" datastore:"type"`
Owner string `json:"owner" datastore:"owner"` Owner string `json:"owner" datastore:"owner"`
Status string `json:"status" datastore:"status"` Status string `json:"status" datastore:"status"`
Running bool `json:"running" datastore:"running"` Workflows []string `json:"workflows" datastore:"workflows"`
Running bool `json:"running" datastore:"running"`
} }
func createFileFromFile(ctx context.Context, bucket *storage.BucketHandle, remotePath, localPath string) error { func createFileFromFile(ctx context.Context, bucket *storage.BucketHandle, remotePath, localPath string) error {
@@ -1332,7 +1333,7 @@ func handleSettings(resp http.ResponseWriter, request *http.Request) {
return return
} }
log.Printf("%s %s", session.Session, UserInfo.Session) //log.Printf("%s %s", session.Session, UserInfo.Session)
if session.Session != UserInfo.Session { if session.Session != UserInfo.Session {
log.Printf("Session %s is not the latest. %s", session.Username, err) log.Printf("Session %s is not the latest. %s", session.Username, err)
resp.WriteHeader(401) resp.WriteHeader(401)
@@ -1394,7 +1395,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
return return
} }
log.Printf("%s %s", session.Session, UserInfo.Session) //log.Printf("%s %s", session.Session, UserInfo.Session)
if session.Session != UserInfo.Session { if session.Session != UserInfo.Session {
log.Printf("Session %s is not the latest. %s", session.Username, err) log.Printf("Session %s is not the latest. %s", session.Username, err)
resp.WriteHeader(401) resp.WriteHeader(401)
@@ -1833,7 +1834,7 @@ func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) {
return return
} }
log.Printf("Existing environments: %s", string(newjson)) //log.Printf("Existing environments: %s", string(newjson))
resp.WriteHeader(200) resp.WriteHeader(200)
resp.Write(newjson) resp.Write(newjson)
@@ -2708,46 +2709,88 @@ func handleNewSchedule(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(`{"success": true, "message": "Created new service"}`)) resp.Write([]byte(`{"success": true, "message": "Created new service"}`))
} }
func handleWebhookRedirect(resp http.ResponseWriter, request *http.Request) { // Does the webhook
func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) {
// 1. Get callback data
// 2. Load the configuration
// 3. Execute the workflow
path := strings.Split(request.URL.String(), "/") path := strings.Split(request.URL.String(), "/")
if len(path) < 4 { if len(path) < 4 {
resp.WriteHeader(403) resp.WriteHeader(403)
resp.Write([]byte(`{"success": false}`))
return return
} }
//http.Redirect(resp, request, "http://www.google.com", 301) // 1. Get config with hookId
//https://europe-west1-shuffler.cloudfunctions.net/webhook_e843bfe2-fc36-4fa5-b682-97cdfa0c0091 //fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId)
ctx := context.Background()
location := strings.Split(request.URL.String(), "/")
body, err := ioutil.ReadAll(request.Body) var hookId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
hookId = location[4]
}
// ID: webhook_<UID>
if len(hookId) != 44 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
return
}
hookId = hookId[8:len(hookId)]
log.Printf("HookID: %s", hookId)
hook, err := getHook(ctx, hookId)
if err != nil { if err != nil {
http.Error(resp, err.Error(), http.StatusInternalServerError) log.Printf("Failed getting hook: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return return
} }
// you can reassign the body if you need to parse it as multipart log.Printf("HOOK FOUND: %#v", hook)
request.Body = ioutil.NopCloser(bytes.NewReader(body)) // Execute the workflow
//executeWorkflow(resp, request)
// create a new url from the raw RequestURI sent by the client //resp.WriteHeader(200)
proxyScheme := "https" //resp.Write([]byte(`{"success": true}`))
url := fmt.Sprintf("%s://%s-%s.cloudfunctions.net/%s", proxyScheme, defaultLocation, gceProject, path[3]) if hook.Status == "stopped" {
log.Println(url) resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Click start to start it"}`)))
proxyReq, err := http.NewRequest(request.Method, url, bytes.NewReader(body))
// We may want to filter some headers, otherwise we could just use a shallow copy
// proxyReq.Header = req.Header
proxyReq.Header = make(http.Header)
for h, val := range request.Header {
proxyReq.Header[h] = val
}
httpClient := &http.Client{}
newresp, err := httpClient.Do(proxyReq)
if err != nil {
http.Error(resp, err.Error(), http.StatusBadGateway)
return return
} }
defer newresp.Body.Close()
if len(hook.Workflows) == 0 {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`)))
return
}
for _, item := range hook.Workflows {
log.Printf("Running for workflow: %s", item)
workflow := Workflow{
ID: "",
}
workflowExecution, executionResp, err := handleExecution(item, workflow, request)
if err == nil {
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
return
}
resp.WriteHeader(500)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp)))
}
} }
// Starts a new webhook // Starts a new webhook
@@ -2780,6 +2823,9 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(`{"success": false}`)) resp.Write([]byte(`{"success": false}`))
return return
} }
log.Println("Data: %s", string(body))
ctx := context.Background() ctx := context.Background()
var requestdata requestData var requestdata requestData
err = yaml.Unmarshal([]byte(body), &requestdata) err = yaml.Unmarshal([]byte(body), &requestdata)
@@ -2827,7 +2873,8 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
} }
hook := Hook{ hook := Hook{
Id: newId, Id: newId,
Workflows: []string{requestdata.Workflow},
Info: Info{ Info: Info{
Name: requestdata.Name, Name: requestdata.Name,
Description: requestdata.Description, Description: requestdata.Description,
@@ -2847,6 +2894,9 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
Running: false, Running: false,
} }
log.Printf("Hello")
// FIXME: Add cloud function execution?
//b, err := json.Marshal(hook) //b, err := json.Marshal(hook)
//if err != nil { //if err != nil {
// log.Printf("Failed marshalling: %s", err) // log.Printf("Failed marshalling: %s", err)
@@ -2855,21 +2905,21 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
// return // return
//} //}
environmentVariables := map[string]string{ //environmentVariables := map[string]string{
"FUNCTION_APIKEY": user.ApiKey, // "FUNCTION_APIKEY": user.ApiKey,
"CALLBACKURL": "https://shuffler.io", // "CALLBACKURL": "https://shuffler.io",
"HOOKID": hook.Id, // "HOOKID": hook.Id,
} //}
applocation := fmt.Sprintf("gs://%s/triggers/webhook.zip", bucketName) //applocation := fmt.Sprintf("gs://%s/triggers/webhook.zip", bucketName)
hookname := fmt.Sprintf("webhook_%s", hook.Id) //hookname := fmt.Sprintf("webhook_%s", hook.Id)
err = deployWebhookFunction(ctx, hookname, defaultLocation, applocation, environmentVariables) //err = deployWebhookFunction(ctx, hookname, defaultLocation, applocation, environmentVariables)
if err != nil { //if err != nil {
log.Printf("Error deploying hook: %s", err) // log.Printf("Error deploying hook: %s", err)
resp.WriteHeader(401) // resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Issue with starting hook. Please wait a second and try again"}`))) // resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Issue with starting hook. Please wait a second and try again"}`)))
return // return
} //}
hook.Status = "running" hook.Status = "running"
hook.Running = true hook.Running = true
@@ -5465,6 +5515,7 @@ func init() {
var err error var err error
ctx := context.Background() ctx := context.Background()
log.Printf("Running INIT process")
dbclient, err = datastore.NewClient(ctx, gceProject) dbclient, err = datastore.NewClient(ctx, gceProject)
if err != nil { if err != nil {
log.Fatalf("DBclient error during init: %s", err) log.Fatalf("DBclient error during init: %s", err)
@@ -5483,11 +5534,11 @@ func init() {
} }
} }
r := mux.NewRouter() log.Printf("Finished INIT")
r.HandleFunc("_ah/health", healthCheckHandler)
r := mux.NewRouter()
r.HandleFunc("/api/v1/_ah/health", healthCheckHandler)
// Webhook redirect to the correct cloud function
r.HandleFunc("/functions/webhooks/{key}", handleWebhookRedirect).Methods("POST", "OPTIONS")
// Sends an email if the right things are specified // Sends an email if the right things are specified
r.HandleFunc("/functions/sendmail", handleSendalert).Methods("POST", "OPTIONS") r.HandleFunc("/functions/sendmail", handleSendalert).Methods("POST", "OPTIONS")
r.HandleFunc("/functions/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS") r.HandleFunc("/functions/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS")
@@ -5552,11 +5603,17 @@ func init() {
r.HandleFunc("/api/v1/workflows/{key}", getSpecificWorkflow).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", getSpecificWorkflow).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", saveWorkflow).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", saveWorkflow).Methods("PUT", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", deleteWorkflow).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", deleteWorkflow).Methods("DELETE", "OPTIONS")
// Triggers
// Webhook redirect to the correct cloud function
r.HandleFunc("/api/v1/hooks/new", handleNewHook).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/hooks/new", handleNewHook).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/hooks/{key}", handleWebhookCallback).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/hooks/{key}/delete", handleDeleteHook).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/hooks/{key}/delete", handleDeleteHook).Methods("DELETE", "OPTIONS")
// Trigger hmm
r.HandleFunc("/api/v1/triggers/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS")
// Weird API's for random things // OpenAPI configuration
r.HandleFunc("/api/v1/verify_swagger", verifySwagger).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/verify_swagger", verifySwagger).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/verify_openapi", verifySwagger).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/verify_openapi", verifySwagger).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/get_openapi_uri", echoOpenapiData).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/get_openapi_uri", echoOpenapiData).Methods("POST", "OPTIONS")
+117 -160
View File
@@ -167,21 +167,23 @@ type Action struct {
// Added environment for location to execute // Added environment for location to execute
type Trigger struct { type Trigger struct {
AppName string `json:"app_name" datastore:"app_name"` AppName string `json:"app_name" datastore:"app_name"`
Status string `json:"status" datastore:"status"` Description string `json:"description" datastore:"description"`
AppVersion string `json:"app_version" datastore:"app_version"` LongDescription string `json:"long_description" datastore:"long_description"`
Errors []string `json:"errors" datastore:"errors"` Status string `json:"status" datastore:"status"`
ID string `json:"id" datastore:"id"` AppVersion string `json:"app_version" datastore:"app_version"`
IsValid bool `json:"is_valid" datastore:"is_valid"` Errors []string `json:"errors" datastore:"errors"`
IsStartNode bool `json:"isStartNode" datastore:"isStartNode"` ID string `json:"id" datastore:"id"`
Label string `json:"label" datastore:"label"` IsValid bool `json:"is_valid" datastore:"is_valid"`
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"` IsStartNode bool `json:"isStartNode" datastore:"isStartNode"`
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false` Label string `json:"label" datastore:"label"`
Environment string `json:"environment" datastore:"environment"` SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
TriggerType string `json:"trigger_type" datastore:"trigger_type"` LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
Name string `json:"name" datastore:"name"` Environment string `json:"environment" datastore:"environment"`
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"` TriggerType string `json:"trigger_type" datastore:"trigger_type"`
Position struct { Name string `json:"name" datastore:"name"`
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"`
Position struct {
X float64 `json:"x" datastore:"x"` X float64 `json:"x" datastore:"x"`
Y float64 `json:"y" datastore:"y"` Y float64 `json:"y" datastore:"y"`
} `json:"position"` } `json:"position"`
@@ -1546,74 +1548,17 @@ func cleanupExecutions(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte("OK")) resp.Write([]byte("OK"))
} }
func executeWorkflow(resp http.ResponseWriter, request *http.Request) { func handleExecution(id string, workflow Workflow, request *http.Request) (WorkflowExecution, string, error) {
cors := handleCors(resp, request) ctx := context.Background()
if cors { if workflow.ID == "" || workflow.ID != id {
return log.Printf("UPDATING WORKFLOW")
} tmpworkflow, err := getWorkflow(ctx, id)
if err != nil {
user, err := handleApiAuthentication(resp, request) log.Printf("Failed getting the workflow locally: %s", err)
if err != nil { return WorkflowExecution{}, "Failed getting workflow", err
log.Printf("Api authentication failed in execute workflow: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
location := strings.Split(request.URL.String(), "/")
var fileId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
} }
fileId = location[4] workflow = *tmpworkflow
}
if len(fileId) != 36 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Workflow ID to execute is not valid"}`))
return
}
//memcacheName := fmt.Sprintf("%s_%s", user.Username, fileId)
var workflow Workflow
ctx := context.Background()
//if item, err := memcache.Get(ctx, memcacheName); err == memcache.ErrCacheMiss {
// // Not in cache
// log.Printf("Workflow %s not in cache.", memcacheName)
tmpworkflow, err := getWorkflow(ctx, fileId)
if err != nil {
log.Printf("Failed getting the workflow locally: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
workflow = *tmpworkflow
//} else if err != nil {
// log.Printf("Error getting item: %v", err)
//} else {
// // FIXME - verify if value is ok? Can unmarshal etc.
// log.Printf("Got workflow %s from cache", fileId)
// err = json.Unmarshal(item.Value, &workflow)
// if err != nil {
// log.Printf("Failed cache unmarshal in executeworkflow for %s", fileId)
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false}`))
// }
//}
// FIXME - have a check for org etc too..
// FIXME - admin check like this? idk
if user.Id != workflow.Owner && user.Role != "admin" && user.Role != "scheduler" && user.Role != fmt.Sprintf("workflow_%s", fileId) {
log.Printf("Wrong user (%s) for workflow %s (execute)", user.Username, workflow.ID)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
} }
if len(workflow.Actions) == 0 { if len(workflow.Actions) == 0 {
@@ -1631,17 +1576,13 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
if !workflow.IsValid { if !workflow.IsValid {
log.Printf("Stopped execution as workflow %s is not valid.", workflow.ID) log.Printf("Stopped execution as workflow %s is not valid.", workflow.ID)
resp.WriteHeader(403) return WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow")
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "workflow %s is invalid"}`, workflow.ID)))
return
} }
workflowBytes, err := json.Marshal(workflow) workflowBytes, err := json.Marshal(workflow)
if err != nil { if err != nil {
log.Printf("Failed workflow unmarshal in execution: %s", err) log.Printf("Failed workflow unmarshal in execution: %s", err)
resp.WriteHeader(http.StatusInternalServerError) return WorkflowExecution{}, "", err
resp.Write([]byte(`{"success": false}`))
return
} }
//log.Println(workflow) //log.Println(workflow)
@@ -1649,29 +1590,22 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
err = json.Unmarshal(workflowBytes, &workflowExecution.Workflow) err = json.Unmarshal(workflowBytes, &workflowExecution.Workflow)
if err != nil { if err != nil {
log.Printf("Failed execution unmarshaling: %s", err) log.Printf("Failed execution unmarshaling: %s", err)
resp.WriteHeader(401) return WorkflowExecution{}, "Failed unmarshal during execution", err
resp.Write([]byte(`{"success": false}`))
return
} }
makeNew := true makeNew := true
if request.Method == "POST" { if request.Method == "POST" {
body, err := ioutil.ReadAll(request.Body) body, err := ioutil.ReadAll(request.Body)
if err != nil { if err != nil {
log.Printf("Failed hook unmarshaling: %s", err) log.Printf("Failed request POST read: %s", err)
resp.WriteHeader(401) return WorkflowExecution{}, "Failed getting body", err
resp.Write([]byte(`{"success": false}`))
return
} }
var execution ExecutionRequest var execution ExecutionRequest
err = json.Unmarshal(body, &execution) err = json.Unmarshal(body, &execution)
if err != nil { if err != nil {
log.Printf("Failed execution POST unmarshaling: %s", err) log.Printf("Failed execution POST unmarshaling: %s", err)
resp.WriteHeader(401) return WorkflowExecution{}, "", err
resp.Write([]byte(`{"success": false}`))
return
} }
// FIXME - this should have "execution_argument" from executeWorkflow frontend // FIXME - this should have "execution_argument" from executeWorkflow frontend
@@ -1706,16 +1640,12 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
oldExecution, err := getWorkflowExecution(ctx, referenceId[0]) oldExecution, err := getWorkflowExecution(ctx, referenceId[0])
if err != nil { if err != nil {
log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err) log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
resp.WriteHeader(401) return WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist."}`, referenceId[0])))
return
} }
if oldExecution.Workflow.ID != fileId { if oldExecution.Workflow.ID != id {
log.Println("Wrong workflowid!") log.Println("Wrong workflowid!")
resp.WriteHeader(401) return WorkflowExecution{}, fmt.Sprintf("Bad ID %s", referenceId), errors.New("Bad ID")
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad ID %s"}`, referenceId)))
return
} }
newResults := []ActionResult{} newResults := []ActionResult{}
@@ -1745,14 +1675,10 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
err = setWorkflowExecution(ctx, *oldExecution) err = setWorkflowExecution(ctx, *oldExecution)
if err != nil { if err != nil {
log.Printf("Error saving workflow execution actionresult setting: %s", err) log.Printf("Error saving workflow execution actionresult setting: %s", err)
resp.WriteHeader(401) return WorkflowExecution{}, fmt.Sprintf("Failed setting workflowexecution actionresult in execution: %s", err), err
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution actionresult in execution: %s"}`, err)))
return
} }
resp.WriteHeader(200) return WorkflowExecution{}, "", nil
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Updating %s with your information."}`, referenceId[0])))
return
} }
} }
@@ -1762,9 +1688,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
oldExecution, err := getWorkflowExecution(ctx, referenceId[0]) oldExecution, err := getWorkflowExecution(ctx, referenceId[0])
if err != nil { if err != nil {
log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err) log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
resp.WriteHeader(401) return WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist."}`, referenceId[0])))
return
} }
workflowExecution = *oldExecution workflowExecution = *oldExecution
@@ -1790,9 +1714,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
// FIXME - regex uuid, and check if already exists? // FIXME - regex uuid, and check if already exists?
if len(workflowExecution.ExecutionId) != 36 { if len(workflowExecution.ExecutionId) != 36 {
log.Printf("Invalid uuid: %s", workflowExecution.ExecutionId) log.Printf("Invalid uuid: %s", workflowExecution.ExecutionId)
resp.WriteHeader(401) return WorkflowExecution{}, "Invalid uuid", err
resp.Write([]byte(`{"success": false, "reason": "Invalid uuid."}`))
return
} }
// FIXME - find owner of workflow // FIXME - find owner of workflow
@@ -1841,9 +1763,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
//log.Println(action.Environment) //log.Println(action.Environment)
if action.Environment == "" { if action.Environment == "" {
resp.WriteHeader(401) return WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!")
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Environment is not defined for %s"}`, action.Name)))
return
} }
newActions = append(newActions, action) newActions = append(newActions, action)
} }
@@ -1875,9 +1795,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
err = setWorkflowExecution(ctx, workflowExecution) err = setWorkflowExecution(ctx, workflowExecution)
if err != nil { if err != nil {
log.Printf("Error saving workflow execution for updates %s: %s", topic, err) log.Printf("Error saving workflow execution for updates %s: %s", topic, err)
resp.WriteHeader(401) return WorkflowExecution{}, "Failed getting workflowexecution", err
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution"}`)))
return
} }
log.Printf("Environments: %#v", environments) log.Printf("Environments: %#v", environments)
@@ -1914,32 +1832,71 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
} }
} }
//body, err := json.Marshal(workflow) return workflowExecution, "", nil
//if err != nil { }
// log.Printf("Failed workflow SET marshalling: %s", err)
// resp.WriteHeader(http.StatusInternalServerError)
// resp.Write([]byte(`{"success": false}`))
// return
//}
//item := &memcache.Item{ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
// Key: memcacheName, cors := handleCors(resp, request)
// Value: body, if cors {
// Expiration: time.Minute * 10, return
//} }
//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)
//}
resp.WriteHeader(200) user, err := handleApiAuthentication(resp, request)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization))) if err != nil {
return log.Printf("Api authentication failed in execute workflow: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
location := strings.Split(request.URL.String(), "/")
var fileId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
fileId = location[4]
}
if len(fileId) != 36 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Workflow ID to execute is not valid"}`))
return
}
//memcacheName := fmt.Sprintf("%s_%s", user.Username, fileId)
ctx := context.Background()
workflow, err := getWorkflow(ctx, fileId)
if err != nil {
log.Printf("Failed getting the workflow locally: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
// FIXME - have a check for org etc too..
// FIXME - admin check like this? idk
if user.Id != workflow.Owner && user.Role != "admin" && user.Role != "scheduler" && user.Role != fmt.Sprintf("workflow_%s", fileId) {
log.Printf("Wrong user (%s) for workflow %s (execute)", user.Username, workflow.ID)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
workflowExecution, executionResp, err := handleExecution(fileId, *workflow, request)
if err == nil {
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
return
}
resp.WriteHeader(500)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp)))
} }
func stopSchedule(resp http.ResponseWriter, request *http.Request) { func stopSchedule(resp http.ResponseWriter, request *http.Request) {
@@ -3434,21 +3391,21 @@ func handleDeleteHook(resp http.ResponseWriter, request *http.Request) {
} }
// This is here to force stop and remove the old webhook // This is here to force stop and remove the old webhook
image := "webhook" //image := "webhook"
err = removeWebhookFunction(ctx, fileId) //err = removeWebhookFunction(ctx, fileId)
if err != nil { //if err != nil {
log.Printf("Function removal issue for %s-%s: %s", image, fileId, err) // log.Printf("Function removal issue for %s-%s: %s", image, fileId, err)
if strings.Contains(err.Error(), "does not exist") { // if strings.Contains(err.Error(), "does not exist") {
resp.WriteHeader(200) // resp.WriteHeader(200)
resp.Write([]byte(`{"success": true, "reason": "Stopped webhook"}`)) // resp.Write([]byte(`{"success": true, "reason": "Stopped webhook"}`))
} else { // } else {
resp.WriteHeader(401) // resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Couldn't stop webhook, please try again later"}`)) // resp.Write([]byte(`{"success": false, "reason": "Couldn't stop webhook, please try again later"}`))
} // }
return // return
} //}
log.Printf("Successfully deleted webhook %s", fileId) log.Printf("Successfully deleted webhook %s", fileId)
resp.WriteHeader(200) resp.WriteHeader(200)
+21 -4
View File
@@ -1,7 +1,7 @@
#curl localhost:5000/api/v1/hooks #curl localhost:5000/api/v1/hooks
# Starts a webhook # Starts a webhook
# curl localhost:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/start #curl localhost:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/start
# Runs a request towards the webhook created # Runs a request towards the webhook created
#curl -XPOST localhost:5002/webhook -d '{"helo": "hi"}' #curl -XPOST localhost:5002/webhook -d '{"helo": "hi"}'
@@ -14,11 +14,28 @@
#curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_982995716e67c3a549092d3a3a7921cd" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"name":"Keyboard Cat"}' -v #curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_982995716e67c3a549092d3a3a7921cd" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"name":"Keyboard Cat"}' -v
## GET HOOK
#curl http://localhost:5001/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
#jcurl http://localhost:5001/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
#curl https://shuffler.io/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" #curl https://shuffler.io/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
#curl -X POST "http://localhost:8080" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"test": {"hello": "HEYOOOO"}}' -v
#curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_3ceff795-ce9a-43a2-a2f5-d4401a6e772d" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut' #curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_3ceff795-ce9a-43a2-a2f5-d4401a6e772d" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut'
curl POST "https://europe-west1-shuffler.cloudfunctions.net/outlooktrigger_be4dbb0a-d396-4544-bc36-e57d1bdb2e40" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut' -vvv #curl POST "https://europe-west1-shuffler.cloudfunctions.net/outlooktrigger_be4dbb0a-d396-4544-bc36-e57d1bdb2e40" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut' -vvv
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
curl -X POST "http://localhost:5001/api/v1/hooks/webhook_9a4efb03-089c-48b7-82f3-f76500a3ebc0" -H "Content-Type:application/json" --data '{"test": {"hello": "HEYOOOO"}}' -v
+2 -2
View File
@@ -6,8 +6,8 @@ docker build . -t webhook
docker run -d \ docker run -d \
-e "HOOKPORT=5001" \ -e "HOOKPORT=5001" \
-e "URIPATH=/webhook" \ -e "URIPATH=/webhook" \
-e "CALLBACKURL=http://192.168.159.151:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/result" \ -e "CALLBACKURL=http://192.168.3.6:5001/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/result" \
-p 5001:5001 \ -p 6000:6000 \
--name webhook \ --name webhook \
-h webhook \ -h webhook \
--restart always \ --restart always \
File diff suppressed because one or more lines are too long