Fixed webhooks for local execution
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# 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
|
||||
Check out the [installation guide](https://github.com/frikky/shuffle/blob/master/install-guide.md)
|
||||
|
||||
@@ -7,6 +7,7 @@ ADD ./go-app/main.go /app
|
||||
ADD ./go-app/walkoff.go /app
|
||||
ADD ./go-app/docker.go /app
|
||||
ADD ./go-app/codegen.go /app
|
||||
ADD ./go-app/webhook.go /app
|
||||
|
||||
ADD ./go-app/go.mod /app
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ require (
|
||||
github.com/go-git/go-billy/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/gorilla/handlers v1.4.2 // indirect
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/h2non/filetype v1.0.12
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
|
||||
@@ -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.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
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/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
|
||||
|
||||
+113
-56
@@ -316,13 +316,14 @@ type HookAction struct {
|
||||
}
|
||||
|
||||
type Hook struct {
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Info Info `json:"info" datastore:"info"`
|
||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Owner string `json:"owner" datastore:"owner"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
Running bool `json:"running" datastore:"running"`
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Info Info `json:"info" datastore:"info"`
|
||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Owner string `json:"owner" datastore:"owner"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
Workflows []string `json:"workflows" datastore:"workflows"`
|
||||
Running bool `json:"running" datastore:"running"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
log.Printf("%s %s", session.Session, UserInfo.Session)
|
||||
//log.Printf("%s %s", session.Session, UserInfo.Session)
|
||||
if session.Session != UserInfo.Session {
|
||||
log.Printf("Session %s is not the latest. %s", session.Username, err)
|
||||
resp.WriteHeader(401)
|
||||
@@ -1394,7 +1395,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("%s %s", session.Session, UserInfo.Session)
|
||||
//log.Printf("%s %s", session.Session, UserInfo.Session)
|
||||
if session.Session != UserInfo.Session {
|
||||
log.Printf("Session %s is not the latest. %s", session.Username, err)
|
||||
resp.WriteHeader(401)
|
||||
@@ -1833,7 +1834,7 @@ func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Existing environments: %s", string(newjson))
|
||||
//log.Printf("Existing environments: %s", string(newjson))
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write(newjson)
|
||||
@@ -2708,46 +2709,88 @@ func handleNewSchedule(resp http.ResponseWriter, request *http.Request) {
|
||||
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(), "/")
|
||||
if len(path) < 4 {
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
//http.Redirect(resp, request, "http://www.google.com", 301)
|
||||
//https://europe-west1-shuffler.cloudfunctions.net/webhook_e843bfe2-fc36-4fa5-b682-97cdfa0c0091
|
||||
// 1. Get config with hookId
|
||||
//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 {
|
||||
http.Error(resp, err.Error(), http.StatusInternalServerError)
|
||||
log.Printf("Failed getting hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// you can reassign the body if you need to parse it as multipart
|
||||
request.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
log.Printf("HOOK FOUND: %#v", hook)
|
||||
// Execute the workflow
|
||||
//executeWorkflow(resp, request)
|
||||
|
||||
// create a new url from the raw RequestURI sent by the client
|
||||
proxyScheme := "https"
|
||||
url := fmt.Sprintf("%s://%s-%s.cloudfunctions.net/%s", proxyScheme, defaultLocation, gceProject, path[3])
|
||||
log.Println(url)
|
||||
|
||||
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)
|
||||
//resp.WriteHeader(200)
|
||||
//resp.Write([]byte(`{"success": true}`))
|
||||
if hook.Status == "stopped" {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Click start to start it"}`)))
|
||||
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
|
||||
@@ -2780,6 +2823,9 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Data: %s", string(body))
|
||||
|
||||
ctx := context.Background()
|
||||
var requestdata requestData
|
||||
err = yaml.Unmarshal([]byte(body), &requestdata)
|
||||
@@ -2827,7 +2873,8 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
hook := Hook{
|
||||
Id: newId,
|
||||
Id: newId,
|
||||
Workflows: []string{requestdata.Workflow},
|
||||
Info: Info{
|
||||
Name: requestdata.Name,
|
||||
Description: requestdata.Description,
|
||||
@@ -2847,6 +2894,9 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
|
||||
Running: false,
|
||||
}
|
||||
|
||||
log.Printf("Hello")
|
||||
|
||||
// FIXME: Add cloud function execution?
|
||||
//b, err := json.Marshal(hook)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed marshalling: %s", err)
|
||||
@@ -2855,21 +2905,21 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
|
||||
// return
|
||||
//}
|
||||
|
||||
environmentVariables := map[string]string{
|
||||
"FUNCTION_APIKEY": user.ApiKey,
|
||||
"CALLBACKURL": "https://shuffler.io",
|
||||
"HOOKID": hook.Id,
|
||||
}
|
||||
//environmentVariables := map[string]string{
|
||||
// "FUNCTION_APIKEY": user.ApiKey,
|
||||
// "CALLBACKURL": "https://shuffler.io",
|
||||
// "HOOKID": hook.Id,
|
||||
//}
|
||||
|
||||
applocation := fmt.Sprintf("gs://%s/triggers/webhook.zip", bucketName)
|
||||
hookname := fmt.Sprintf("webhook_%s", hook.Id)
|
||||
err = deployWebhookFunction(ctx, hookname, defaultLocation, applocation, environmentVariables)
|
||||
if err != nil {
|
||||
log.Printf("Error deploying hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Issue with starting hook. Please wait a second and try again"}`)))
|
||||
return
|
||||
}
|
||||
//applocation := fmt.Sprintf("gs://%s/triggers/webhook.zip", bucketName)
|
||||
//hookname := fmt.Sprintf("webhook_%s", hook.Id)
|
||||
//err = deployWebhookFunction(ctx, hookname, defaultLocation, applocation, environmentVariables)
|
||||
//if err != nil {
|
||||
// log.Printf("Error deploying hook: %s", err)
|
||||
// resp.WriteHeader(401)
|
||||
// resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Issue with starting hook. Please wait a second and try again"}`)))
|
||||
// return
|
||||
//}
|
||||
|
||||
hook.Status = "running"
|
||||
hook.Running = true
|
||||
@@ -5465,6 +5515,7 @@ func init() {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
|
||||
log.Printf("Running INIT process")
|
||||
dbclient, err = datastore.NewClient(ctx, gceProject)
|
||||
if err != nil {
|
||||
log.Fatalf("DBclient error during init: %s", err)
|
||||
@@ -5483,11 +5534,11 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("_ah/health", healthCheckHandler)
|
||||
log.Printf("Finished INIT")
|
||||
|
||||
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
|
||||
r.HandleFunc("/functions/sendmail", handleSendalert).Methods("POST", "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}", saveWorkflow).Methods("PUT", "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/{key}", handleWebhookCallback).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/hooks/{key}/delete", handleDeleteHook).Methods("DELETE", "OPTIONS")
|
||||
|
||||
// Trigger hmm
|
||||
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_openapi", verifySwagger).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/get_openapi_uri", echoOpenapiData).Methods("POST", "OPTIONS")
|
||||
|
||||
+117
-160
@@ -167,21 +167,23 @@ type Action struct {
|
||||
|
||||
// Added environment for location to execute
|
||||
type Trigger struct {
|
||||
AppName string `json:"app_name" datastore:"app_name"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
AppVersion string `json:"app_version" datastore:"app_version"`
|
||||
Errors []string `json:"errors" datastore:"errors"`
|
||||
ID string `json:"id" datastore:"id"`
|
||||
IsValid bool `json:"is_valid" datastore:"is_valid"`
|
||||
IsStartNode bool `json:"isStartNode" datastore:"isStartNode"`
|
||||
Label string `json:"label" datastore:"label"`
|
||||
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
||||
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
||||
Environment string `json:"environment" datastore:"environment"`
|
||||
TriggerType string `json:"trigger_type" datastore:"trigger_type"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"`
|
||||
Position struct {
|
||||
AppName string `json:"app_name" datastore:"app_name"`
|
||||
Description string `json:"description" datastore:"description"`
|
||||
LongDescription string `json:"long_description" datastore:"long_description"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
AppVersion string `json:"app_version" datastore:"app_version"`
|
||||
Errors []string `json:"errors" datastore:"errors"`
|
||||
ID string `json:"id" datastore:"id"`
|
||||
IsValid bool `json:"is_valid" datastore:"is_valid"`
|
||||
IsStartNode bool `json:"isStartNode" datastore:"isStartNode"`
|
||||
Label string `json:"label" datastore:"label"`
|
||||
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
||||
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
||||
Environment string `json:"environment" datastore:"environment"`
|
||||
TriggerType string `json:"trigger_type" datastore:"trigger_type"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"`
|
||||
Position struct {
|
||||
X float64 `json:"x" datastore:"x"`
|
||||
Y float64 `json:"y" datastore:"y"`
|
||||
} `json:"position"`
|
||||
@@ -1546,74 +1548,17 @@ func cleanupExecutions(resp http.ResponseWriter, request *http.Request) {
|
||||
resp.Write([]byte("OK"))
|
||||
}
|
||||
|
||||
func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := handleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := handleApiAuthentication(resp, request)
|
||||
if err != nil {
|
||||
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
|
||||
func handleExecution(id string, workflow Workflow, request *http.Request) (WorkflowExecution, string, error) {
|
||||
ctx := context.Background()
|
||||
if workflow.ID == "" || workflow.ID != id {
|
||||
log.Printf("UPDATING WORKFLOW")
|
||||
tmpworkflow, err := getWorkflow(ctx, id)
|
||||
if err != nil {
|
||||
log.Printf("Failed getting the workflow locally: %s", err)
|
||||
return WorkflowExecution{}, "Failed getting workflow", err
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
workflow = *tmpworkflow
|
||||
}
|
||||
|
||||
if len(workflow.Actions) == 0 {
|
||||
@@ -1631,17 +1576,13 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
if !workflow.IsValid {
|
||||
log.Printf("Stopped execution as workflow %s is not valid.", workflow.ID)
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "workflow %s is invalid"}`, workflow.ID)))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow")
|
||||
}
|
||||
|
||||
workflowBytes, err := json.Marshal(workflow)
|
||||
if err != nil {
|
||||
log.Printf("Failed workflow unmarshal in execution: %s", err)
|
||||
resp.WriteHeader(http.StatusInternalServerError)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
return WorkflowExecution{}, "", err
|
||||
}
|
||||
|
||||
//log.Println(workflow)
|
||||
@@ -1649,29 +1590,22 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
err = json.Unmarshal(workflowBytes, &workflowExecution.Workflow)
|
||||
if err != nil {
|
||||
log.Printf("Failed execution unmarshaling: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
return WorkflowExecution{}, "Failed unmarshal during execution", err
|
||||
}
|
||||
|
||||
makeNew := true
|
||||
if request.Method == "POST" {
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
log.Printf("Failed hook unmarshaling: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
log.Printf("Failed request POST read: %s", err)
|
||||
return WorkflowExecution{}, "Failed getting body", err
|
||||
}
|
||||
|
||||
var execution ExecutionRequest
|
||||
err = json.Unmarshal(body, &execution)
|
||||
if err != nil {
|
||||
log.Printf("Failed execution POST unmarshaling: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
return WorkflowExecution{}, "", err
|
||||
}
|
||||
|
||||
// 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])
|
||||
if err != nil {
|
||||
log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist."}`, referenceId[0])))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
|
||||
}
|
||||
|
||||
if oldExecution.Workflow.ID != fileId {
|
||||
if oldExecution.Workflow.ID != id {
|
||||
log.Println("Wrong workflowid!")
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad ID %s"}`, referenceId)))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf("Bad ID %s", referenceId), errors.New("Bad ID")
|
||||
}
|
||||
|
||||
newResults := []ActionResult{}
|
||||
@@ -1745,14 +1675,10 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
err = setWorkflowExecution(ctx, *oldExecution)
|
||||
if err != nil {
|
||||
log.Printf("Error saving workflow execution actionresult setting: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution actionresult in execution: %s"}`, err)))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf("Failed setting workflowexecution actionresult in execution: %s", err), err
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Updating %s with your information."}`, referenceId[0])))
|
||||
return
|
||||
return WorkflowExecution{}, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1762,9 +1688,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
oldExecution, err := getWorkflowExecution(ctx, referenceId[0])
|
||||
if err != nil {
|
||||
log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist."}`, referenceId[0])))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err
|
||||
}
|
||||
|
||||
workflowExecution = *oldExecution
|
||||
@@ -1790,9 +1714,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
// FIXME - regex uuid, and check if already exists?
|
||||
if len(workflowExecution.ExecutionId) != 36 {
|
||||
log.Printf("Invalid uuid: %s", workflowExecution.ExecutionId)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Invalid uuid."}`))
|
||||
return
|
||||
return WorkflowExecution{}, "Invalid uuid", err
|
||||
}
|
||||
|
||||
// FIXME - find owner of workflow
|
||||
@@ -1841,9 +1763,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
//log.Println(action.Environment)
|
||||
|
||||
if action.Environment == "" {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Environment is not defined for %s"}`, action.Name)))
|
||||
return
|
||||
return WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!")
|
||||
}
|
||||
newActions = append(newActions, action)
|
||||
}
|
||||
@@ -1875,9 +1795,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
err = setWorkflowExecution(ctx, workflowExecution)
|
||||
if err != nil {
|
||||
log.Printf("Error saving workflow execution for updates %s: %s", topic, err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting workflowexecution"}`)))
|
||||
return
|
||||
return WorkflowExecution{}, "Failed getting workflowexecution", err
|
||||
}
|
||||
|
||||
log.Printf("Environments: %#v", environments)
|
||||
@@ -1914,32 +1832,71 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
//body, err := json.Marshal(workflow)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed workflow SET marshalling: %s", err)
|
||||
// resp.WriteHeader(http.StatusInternalServerError)
|
||||
// resp.Write([]byte(`{"success": false}`))
|
||||
// return
|
||||
//}
|
||||
return workflowExecution, "", nil
|
||||
}
|
||||
|
||||
//item := &memcache.Item{
|
||||
// Key: memcacheName,
|
||||
// Value: body,
|
||||
// Expiration: time.Minute * 10,
|
||||
//}
|
||||
//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)
|
||||
//}
|
||||
func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := handleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
|
||||
return
|
||||
user, err := handleApiAuthentication(resp, request)
|
||||
if err != nil {
|
||||
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) {
|
||||
@@ -3434,21 +3391,21 @@ func handleDeleteHook(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
// This is here to force stop and remove the old webhook
|
||||
image := "webhook"
|
||||
err = removeWebhookFunction(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Function removal issue for %s-%s: %s", image, fileId, err)
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true, "reason": "Stopped webhook"}`))
|
||||
//image := "webhook"
|
||||
//err = removeWebhookFunction(ctx, fileId)
|
||||
//if err != nil {
|
||||
// log.Printf("Function removal issue for %s-%s: %s", image, fileId, err)
|
||||
// if strings.Contains(err.Error(), "does not exist") {
|
||||
// resp.WriteHeader(200)
|
||||
// resp.Write([]byte(`{"success": true, "reason": "Stopped webhook"}`))
|
||||
|
||||
} else {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Couldn't stop webhook, please try again later"}`))
|
||||
}
|
||||
// } else {
|
||||
// resp.WriteHeader(401)
|
||||
// resp.Write([]byte(`{"success": false, "reason": "Couldn't stop webhook, please try again later"}`))
|
||||
// }
|
||||
|
||||
return
|
||||
}
|
||||
// return
|
||||
//}
|
||||
|
||||
log.Printf("Successfully deleted webhook %s", fileId)
|
||||
resp.WriteHeader(200)
|
||||
|
||||
+21
-4
@@ -1,7 +1,7 @@
|
||||
#curl localhost:5000/api/v1/hooks
|
||||
|
||||
# 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
|
||||
#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
|
||||
|
||||
## 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 -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 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
|
||||
|
||||
@@ -6,8 +6,8 @@ docker build . -t webhook
|
||||
docker run -d \
|
||||
-e "HOOKPORT=5001" \
|
||||
-e "URIPATH=/webhook" \
|
||||
-e "CALLBACKURL=http://192.168.159.151:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/result" \
|
||||
-p 5001:5001 \
|
||||
-e "CALLBACKURL=http://192.168.3.6:5001/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/result" \
|
||||
-p 6000:6000 \
|
||||
--name webhook \
|
||||
-h webhook \
|
||||
--restart always \
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user