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
[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)
+1
View File
@@ -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
+1
View File
@@ -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
+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.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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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