Made major changes to import/export and workflow saving

This commit is contained in:
frikky
2021-03-15 07:41:02 +01:00
parent d662e9d6aa
commit cb319380f6
5 changed files with 526 additions and 455 deletions
+8 -8
View File
@@ -2447,7 +2447,7 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(loginData))
return
} else {
log.Printf("User session is empty - create one!")
log.Printf("[INFO] User session is empty - create one!")
sessionToken := uuid.NewV4().String()
expiration := time.Now().Add(3600 * time.Second)
@@ -5797,7 +5797,7 @@ func handleCloudExecutionOnprem(workflowId, startNode, executionSource, executio
var execution ExecutionRequest
err = json.Unmarshal([]byte(parsedArgument), &execution)
if err == nil {
log.Printf("FOUND EXEC %#v", execution)
//log.Printf("[INFO] FOUND EXEC %#v", execution)
if len(execution.ExecutionArgument) > 0 {
parsedArgument := strings.Replace(string(execution.ExecutionArgument), "\\\"", "\"", -1)
log.Printf("New exec argument: %s", execution.ExecutionArgument)
@@ -5822,7 +5822,7 @@ func handleCloudExecutionOnprem(workflowId, startNode, executionSource, executio
return err
}
log.Println(string(b))
//log.Println(string(b))
newRequest := &http.Request{
URL: &url.URL{},
Method: "POST",
@@ -5864,8 +5864,8 @@ func handleCloudJob(job CloudSyncJob) error {
}
emails, err := getOutlookEmail(outlookClient, maildata)
log.Printf("EMAILS: %d", len(emails))
log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
//log.Printf("EMAILS: %d", len(emails))
//log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
//type FullEmail struct {
email := FullEmail{}
@@ -5879,7 +5879,7 @@ func handleCloudJob(job CloudSyncJob) error {
return err
}
log.Printf("Should handle webhook for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
log.Printf("[INFO] Should handle outlook webhook for workflow %s with start node %s and data of length %d", job.PrimaryItemId, job.SecondaryItem, len(job.ThirdItem))
err = handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "outlook", string(emailBytes))
if err != nil {
log.Printf("Failed executing workflow from cloud outlook hook: %s", err)
@@ -5889,7 +5889,7 @@ func handleCloudJob(job CloudSyncJob) error {
}
} else if job.Type == "webhook" {
if job.Action == "execute" {
log.Printf("Should handle webhook for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
log.Printf("Should handle normal webhook for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
err := handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "webhook", job.ThirdItem)
if err != nil {
log.Printf("Failed executing workflow from cloud hook: %s", err)
@@ -6053,7 +6053,7 @@ func remoteOrgJobController(org Org, body []byte) error {
}
if len(responseData.Jobs) > 0 {
log.Printf("[INFO] Remote JOB ret: %s", string(body))
//log.Printf("[INFO] Remote JOB ret: %s", string(body))
log.Printf("Got job with reason %s and %d job(s)", responseData.Reason, len(responseData.Jobs))
}
+10 -10
View File
@@ -126,7 +126,7 @@ func getOutlookEmail(client *http.Client, maildata MailData) ([]FullEmail, error
//messageId := email.Resourcedata.ID
//requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/%s", messageId)
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s", email.Resource)
log.Printf("URL: %#v", requestUrl)
//log.Printf("Outlook email URL: %#v", requestUrl)
ret, err := client.Get(requestUrl)
if err != nil {
@@ -141,8 +141,8 @@ func getOutlookEmail(client *http.Client, maildata MailData) ([]FullEmail, error
}
//type FullEmail struct {
log.Printf("[INFO] EMAIL Body: %s", string(body))
log.Printf("[INFO] Status email: %d", ret.StatusCode)
//log.Printf("[INFO] EMAIL Body: %s", string(body))
//log.Printf("[INFO] Status email: %d", ret.StatusCode)
if ret.StatusCode != 200 {
return []FullEmail{}, err
}
@@ -724,7 +724,7 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) {
log.Printf("Failed finding org %s: %s", org.Id, err)
return
}
log.Printf("[INFO] Starting cloud configuration TO STOP trigger %s in org %s", trigger.Id, org.Id)
log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
action := CloudSyncJob{
Type: "outlook",
@@ -732,7 +732,7 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) {
OrgId: org.Id,
PrimaryItemId: trigger.Id,
SecondaryItem: trigger.Start,
ThirdItem: trigger.WorkflowId,
ThirdItem: workflowId,
}
err = executeCloudAction(action, org.SyncConfig.Apikey)
@@ -982,8 +982,8 @@ func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) {
}
emails, err := getOutlookEmail(outlookClient, maildata)
log.Printf("EMAILS: %d", len(emails))
log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
log.Printf("[INFO] EMAILS: %d. If this is more than 1, please contact frikky@shuffler.io", len(emails))
//log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
//type FullEmail struct {
email := FullEmail{}
@@ -1087,14 +1087,14 @@ func handleOutlookSubRemoval(ctx context.Context, user User, workflowId, trigger
}
if runningEnvironment != "cloud" {
log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD")
log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId)
org, err := getOrg(ctx, user.ActiveOrg.Id)
if err != nil {
log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err)
return err
}
log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s", trigger.Id, org.Id)
log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
action := CloudSyncJob{
Type: "outlook",
Action: "stop",
@@ -1128,7 +1128,7 @@ func handleOutlookSubRemoval(ctx context.Context, user User, workflowId, trigger
if err == nil {
for _, sub := range curSubscriptions.Value {
if sub.NotificationURL == notificationURL {
log.Printf("[INFO] Removing subscription %s from o365", sub.Id)
log.Printf("[INFO] Removing subscription %s from o365 for workflow %s", sub.Id, workflowId)
removeOutlookSubscription(outlookClient, sub.Id)
}
}
+442 -400
View File
@@ -424,7 +424,7 @@ type Workflow struct {
Value string `json:"value" datastore:"value,noindex"`
} `json:"execution_variables,omitempty" datastore:"execution_variables"`
ExecutionEnvironment string `json:"execution_environment" datastore:"execution_environment"`
PreviouslySaved bool `json:"first_save" datastore:"first_save"`
PreviouslySaved bool `json:"previously_saved" datastore:"first_save"`
Categories Categories `json:"categories" datastore:"categories"`
ExampleArgument string `json:"example_argument" datastore:"example_argument,noindex"`
}
@@ -1810,7 +1810,7 @@ func setNewWorkflow(resp http.ResponseWriter, request *http.Request) {
action.IsValid = true
}
action.LargeImage = ""
//action.LargeImage = ""
newActions = append(newActions, action)
}
@@ -1962,7 +1962,7 @@ func setNewWorkflow(resp http.ResponseWriter, request *http.Request) {
return
}
log.Printf("Saved new workflow %s with name %s", workflow.ID, workflow.Name)
log.Printf("[INFO] Saved new workflow %s with name %s", workflow.ID, workflow.Name)
//memcacheName := fmt.Sprintf("%s_workflows", user.Username)
//memcache.Delete(ctx, memcacheName)
@@ -2047,7 +2047,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) {
}
// FIXME - maybe delete workflow executions
log.Printf("Should delete workflow %s", fileId)
log.Printf("[INFO] Should have deleted workflow %s", fileId)
err = DeleteKey(ctx, "workflow", fileId)
if err != nil {
log.Printf("Failed deleting key %s", fileId)
@@ -2244,6 +2244,8 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
return
}
//log.Printf("SAVED: %#v", workflow.PreviouslySaved)
// FIXME - auth and check if they should have access
if fileId != workflow.ID {
log.Printf("Path and request ID are not matching: %s:%s.", fileId, workflow.ID)
@@ -2258,7 +2260,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
}
if len(workflow.ExecutingOrg.Id) == 0 {
log.Printf("Setting executing org for workflow")
log.Printf("[INFO] Setting executing org for workflow")
user.ActiveOrg.Users = []User{}
workflow.ExecutingOrg = user.ActiveOrg
}
@@ -2281,18 +2283,23 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
}
if action.Environment == "" {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "An environment for %s is required"}`, action.Label)))
return
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "An environment for %s is required"}`, action.Label)))
return
}
action.IsValid = true
}
// FIXME: Have a good way of tracking errors. ID's or similar.
if !action.IsValid && len(action.Errors) > 0 {
log.Printf("Node %s is invalid and needs to be remade. Errors: %s", action.Label, strings.Join(action.Errors, "\n"))
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Node %s is invalid and needs to be remade."}`, action.Label)))
return
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Node %s is invalid and needs to be remade."}`, action.Label)))
return
}
action.IsValid = true
action.Errors = []string{}
}
@@ -2301,7 +2308,418 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
newActions = append(newActions, action)
}
log.Printf("PRE SAVECHECK")
newTriggers := []Trigger{}
for _, trigger := range workflow.Triggers {
log.Printf("[INFO] Trigger %s: %s", trigger.TriggerType, trigger.Status)
// Check if it's actually running
// FIXME: Do this for other triggers too
if trigger.TriggerType == "SCHEDULE" && trigger.Status != "uninitialized" {
schedule, err := getSchedule(ctx, trigger.ID)
if err != nil {
trigger.Status = "stopped"
} else if schedule.Id == "" {
trigger.Status = "stopped"
}
} else if trigger.TriggerType == "SUBFLOW" {
for index, param := range trigger.Parameters {
if len(param.Value) == 0 && param.Name != "argument" {
//log.Printf("Param: %#v", param)
if param.Name == "user_apikey" {
apikey := ""
if len(user.ApiKey) > 0 {
apikey = user.ApiKey
} else {
user, err = generateApikey(ctx, user)
if err != nil {
workflow.IsValid = false
workflow.Errors = []string{"Trigger is missing a parameter: %s", param.Name}
log.Printf("No type specified for user input node")
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Trigger %s is missing the parameter %s"}`, trigger.Label, param.Name)))
return
}
}
apikey = user.ApiKey
}
log.Printf("[INFO] Set apikey in subflow trigger for user during save")
trigger.Parameters[index].Value = apikey
} else {
workflow.IsValid = false
workflow.Errors = []string{"Trigger is missing a parameter: %s", param.Name}
log.Printf("No type specified for user input node")
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Trigger %s is missing the parameter %s"}`, trigger.Label, param.Name)))
return
}
}
}
}
} else if trigger.TriggerType == "WEBHOOK" && trigger.Status != "uninitialized" {
hook, err := getHook(ctx, trigger.ID)
if err != nil {
log.Printf("Failed getting webhook")
trigger.Status = "stopped"
} else if hook.Id == "" {
trigger.Status = "stopped"
}
} else if trigger.TriggerType == "USERINPUT" {
// E.g. check email
sms := ""
email := ""
triggerType := ""
triggerInformation := ""
for _, item := range trigger.Parameters {
if item.Name == "alertinfo" {
triggerInformation = item.Value
} else if item.Name == "type" {
triggerType = item.Value
} else if item.Name == "email" {
email = item.Value
} else if item.Name == "sms" {
sms = item.Value
}
}
if len(triggerType) == 0 {
log.Printf("No type specified for user input node")
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No contact option specified in user input"}`)))
return
}
}
// FIXME: This is not the right time to send them, BUT it's well served for testing. Save -> send email / sms
_ = triggerInformation
if strings.Contains(triggerType, "email") {
if email == "test@test.com" {
log.Printf("Email isn't specified during save.")
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Email field in user input can't be empty"}`)))
return
}
}
log.Printf("Should send email to %s during execution.", email)
}
if strings.Contains(triggerType, "sms") {
if sms == "0000000" {
log.Printf("Email isn't specified during save.")
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "SMS field in user input can't be empty"}`)))
return
}
}
log.Printf("Should send SMS to %s during execution.", sms)
}
}
//log.Println("TRIGGERS")
allNodes = append(allNodes, trigger.ID)
newTriggers = append(newTriggers, trigger)
}
workflow.Triggers = newTriggers
if len(workflow.Actions) == 0 {
workflow.Actions = []Action{}
}
if len(workflow.Branches) == 0 {
workflow.Branches = []Branch{}
}
if len(workflow.Triggers) == 0 {
workflow.Triggers = []Trigger{}
}
if len(workflow.Errors) == 0 {
workflow.Errors = []string{}
}
//log.Printf("PRE VARIABLES")
for _, variable := range workflow.WorkflowVariables {
if len(variable.Value) == 0 {
log.Printf("[WARNING] Variable %s is empty!", variable.Name)
workflow.Errors = append(workflow.Errors, fmt.Sprintf("Variable %s is empty!", variable.Name))
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Variable %s can't be empty"}`, variable.Name)))
//return
}
}
if len(workflow.ExecutionVariables) > 0 {
log.Printf("[INFO] Found %d execution variable(s)", len(workflow.ExecutionVariables))
}
if len(workflow.WorkflowVariables) > 0 {
log.Printf("[INFO] Found %d workflow variable(s)", len(workflow.WorkflowVariables))
}
// FIXME - do actual checks ROFL
// FIXME - minor issues with e.g. hello world and self.console_logger
// Nodechecks
foundNodes := []string{}
for _, node := range allNodes {
for _, branch := range workflow.Branches {
//log.Println("branch")
//log.Println(node)
//log.Println(branch.DestinationID)
if node == branch.DestinationID || node == branch.SourceID {
foundNodes = append(foundNodes, node)
break
}
}
}
// FIXME - append all nodes (actions, triggers etc) to one single array here
//log.Printf("PRE VARIABLES")
if len(foundNodes) != len(allNodes) || len(workflow.Actions) <= 0 {
// This shit takes a few seconds lol
if !workflow.IsValid {
oldworkflow, err := getWorkflow(ctx, fileId)
if err != nil {
log.Printf("Workflow %s doesn't exist - oldworkflow.", fileId)
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Item already exists."}`))
return
}
}
oldworkflow.IsValid = false
err = setWorkflow(ctx, *oldworkflow, fileId)
if err != nil {
log.Printf("Failed saving workflow to database: %s", err)
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
}
}
// FIXME - more checks here - force reload of data or something
//if len(allNodes) == 0 {
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false, "reason": "Please insert a node"}`))
// return
//}
// Allowed with only a start node
//if len(allNodes) != 1 {
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false, "reason": "There are nodes with no branches"}`))
// return
//}
}
// FIXME - might be a sploit to run someone elses app if getAllWorkflowApps
// doesn't check sharing=true
// Have to do it like this to add the user's apps
//log.Println("Apps set starting")
//log.Printf("EXIT ON ERROR: %#v", workflow.Configuration.ExitOnError)
//workflowapps, apperr := getAllWorkflowApps(ctx, 500)
// Started getting the single apps, but if it's weird, this is faster
// 1. Check workflow.Start
// 2. Check if any node has "isStartnode"
if len(workflow.Actions) > 0 {
index := -1
for indexFound, action := range workflow.Actions {
//log.Println("Apps set done")
if workflow.Start == action.ID {
index = indexFound
}
}
if index >= 0 {
workflow.Actions[0].IsStartNode = true
} else {
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "You need to set a startnode."}`)))
return
}
}
}
allAuths, err := getAllWorkflowAppAuth(ctx, user.ActiveOrg.Id)
if userErr != nil {
log.Printf("Api authentication failed in get all apps: %s", userErr)
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
}
// Check every app action and param to see whether they exist
//log.Printf("PRE ACTIONS 2")
newActions = []Action{}
for _, action := range workflow.Actions {
reservedApps := []string{
"0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e",
}
//log.Printf("%s Action execution var: %s", action.Label, action.ExecutionVariable.Name)
builtin := false
for _, id := range reservedApps {
if id == action.AppID {
builtin = true
break
}
}
// Check auth
// 1. Find the auth in question
// 2. Update the node and workflow info in the auth
// 3. Get the values in the auth and add them to the action values
if len(action.AuthenticationId) > 0 {
authFound := false
for _, auth := range allAuths {
if auth.Id == action.AuthenticationId {
authFound = true
// Updates the auth item itself IF necessary
go updateAppAuth(auth, workflow.ID, action.ID, true)
break
}
}
if !authFound {
log.Printf("App auth %s doesn't exist. Setting error", action.AuthenticationId)
workflow.Errors = append(workflow.Errors, fmt.Sprintf("App authentication for %s doesn't exist!", action.AppName))
workflow.IsValid = false
action.Errors = append(action.Errors, "App authentication doesn't exist")
action.IsValid = false
action.AuthenticationId = ""
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "App auth %s doesn't exist"}`, action.AuthenticationId)))
//return
}
}
if builtin {
newActions = append(newActions, action)
} else {
curapp := WorkflowApp{}
// FIXME - can this work with ONLY AppID?
for _, app := range workflowapps {
if app.ID == action.AppID {
curapp = app
break
}
// Has to NOT be generated
if app.Name == action.AppName && app.AppVersion == action.AppVersion {
curapp = app
break
}
}
// Check to see if the whole app is valid
if curapp.Name != action.AppName {
workflow.Errors = append(workflow.Errors, fmt.Sprintf("App %s doesn't exist", action.AppName))
action.Errors = append(action.Errors, "This app doesn't exist.")
action.IsValid = false
workflow.IsValid = false
// Append with errors
newActions = append(newActions, action)
log.Printf("App %s doesn't exist. Adding as error.", action.AppName)
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "App %s doesn't exist"}`, action.AppName)))
//return
} else {
// Check tosee if the appaction is valid
curappaction := WorkflowAppAction{}
for _, curAction := range curapp.Actions {
if action.Name == curAction.Name {
curappaction = curAction
break
}
}
// Check to see if the action is valid
if curappaction.Name != action.Name {
log.Printf("[ERROR] Action %s in app %s doesn't exist.", action.Name, curapp.Name)
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Action %s in app %s doesn't exist"}`, action.Name, curapp.Name)))
return
}
}
// FIXME - check all parameters to see if they're valid
// Includes checking required fields
newParams := []WorkflowAppActionParameter{}
for _, param := range curappaction.Parameters {
found := false
// Handles check for parameter exists + value not empty in used fields
for _, actionParam := range action.Parameters {
if actionParam.Name == param.Name {
found = true
if actionParam.Value == "" && actionParam.Variant == "STATIC_VALUE" && actionParam.Required == true {
log.Printf("[WARNING] Appaction %s with required param '%s' is empty. Can't save.", action.Name, param.Name)
//if workflow.PreviouslySaved {
// resp.WriteHeader(401)
// resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Appaction %s in app %s with required param '%s' is empty.", "node_id": "%s"}`, action.Name, action.AppName, param.Name, action.ID)))
// return
//} else {
thisError := fmt.Sprintf("Missing parameter %s", param.Name)
action.Errors = append(action.Errors, thisError)
workflow.Errors = append(workflow.Errors, thisError)
action.IsValid = false
}
if actionParam.Variant == "" {
actionParam.Variant = "STATIC_VALUE"
}
newParams = append(newParams, actionParam)
break
}
}
// Handles check for required params
if !found && param.Required {
log.Printf("Appaction %s with required param %s doesn't exist.", action.Name, param.Name)
thisError := fmt.Sprintf("Parameter %s is required", param.Name)
action.Errors = append(action.Errors, thisError)
workflow.Errors = append(workflow.Errors, thisError)
action.IsValid = false
//newActions = append(newActions, action)
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Appaction %s with required param '%s' is empty."}`, action.Name, param.Name)))
//return
}
}
action.Parameters = newParams
newActions = append(newActions, action)
}
}
}
//log.Printf("PRE SAVECHECK")
if !workflow.PreviouslySaved {
log.Printf("[WORKFLOW INIT] NOT PREVIOUSLY SAVED - SET ACTION AUTH!")
//AuthenticationId string `json:"authentication_id,omitempty" datastore:"authentication_id"`
@@ -2429,7 +2847,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
newActions = actionFixing
} else {
log.Printf("Err: %s - %s", err, apperr)
log.Printf("FirstSave error: %s - %s", err, apperr)
//workflowapps, apperr := getAllWorkflowApps(ctx, 100)
//allAuths, err := getAllWorkflowAppAuth(ctx, user.ActiveOrg.Id)
}
@@ -2437,386 +2855,8 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
workflow.PreviouslySaved = true
}
log.Printf("PRE TRIGGERS")
workflow.Actions = newActions
newTriggers := []Trigger{}
for _, trigger := range workflow.Triggers {
log.Printf("[INFO] Trigger %s: %s", trigger.TriggerType, trigger.Status)
// Check if it's actually running
// FIXME: Do this for other triggers too
if trigger.TriggerType == "SCHEDULE" && trigger.Status != "uninitialized" {
schedule, err := getSchedule(ctx, trigger.ID)
if err != nil {
trigger.Status = "stopped"
} else if schedule.Id == "" {
trigger.Status = "stopped"
}
} else if trigger.TriggerType == "SUBFLOW" {
for index, param := range trigger.Parameters {
if len(param.Value) == 0 && param.Name != "argument" {
log.Printf("Param: %#v", param)
if param.Name == "user_apikey" {
apikey := ""
if len(user.ApiKey) > 0 {
apikey = user.ApiKey
} else {
user, err = generateApikey(ctx, user)
if err != nil {
workflow.IsValid = false
workflow.Errors = []string{"Trigger is missing a parameter: %s", param.Name}
log.Printf("No type specified for user input node")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Trigger %s is missing the parameter %s"}`, trigger.Label, param.Name)))
return
}
apikey = user.ApiKey
}
log.Printf("[INFO] Set apikey in subflow trigger for user during save")
trigger.Parameters[index].Value = apikey
} else {
workflow.IsValid = false
workflow.Errors = []string{"Trigger is missing a parameter: %s", param.Name}
log.Printf("No type specified for user input node")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Trigger %s is missing the parameter %s"}`, trigger.Label, param.Name)))
return
}
}
}
} else if trigger.TriggerType == "WEBHOOK" && trigger.Status != "uninitialized" {
hook, err := getHook(ctx, trigger.ID)
if err != nil {
log.Printf("Failed getting webhook")
trigger.Status = "stopped"
} else if hook.Id == "" {
trigger.Status = "stopped"
}
} else if trigger.TriggerType == "USERINPUT" {
// E.g. check email
sms := ""
email := ""
triggerType := ""
triggerInformation := ""
for _, item := range trigger.Parameters {
if item.Name == "alertinfo" {
triggerInformation = item.Value
} else if item.Name == "type" {
triggerType = item.Value
} else if item.Name == "email" {
email = item.Value
} else if item.Name == "sms" {
sms = item.Value
}
}
if len(triggerType) == 0 {
log.Printf("No type specified for user input node")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No contact option specified in user input"}`)))
return
}
// FIXME: This is not the right time to send them, BUT it's well served for testing. Save -> send email / sms
_ = triggerInformation
if strings.Contains(triggerType, "email") {
if email == "test@test.com" {
log.Printf("Email isn't specified during save.")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Email field in user input can't be empty"}`)))
return
}
log.Printf("Should send email to %s during execution.", email)
}
if strings.Contains(triggerType, "sms") {
if sms == "0000000" {
log.Printf("Email isn't specified during save.")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "SMS field in user input can't be empty"}`)))
return
}
log.Printf("Should send SMS to %s during execution.", sms)
}
}
//log.Println("TRIGGERS")
allNodes = append(allNodes, trigger.ID)
newTriggers = append(newTriggers, trigger)
}
workflow.Triggers = newTriggers
//log.Printf("PRE VARIABLES")
for _, variable := range workflow.WorkflowVariables {
if len(variable.Value) == 0 {
log.Printf("Can't have an empty variable: %s", variable.Name)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Variable %s can't be empty"}`, variable.Name)))
return
}
}
if len(workflow.Actions) == 0 {
workflow.Actions = []Action{}
}
if len(workflow.Branches) == 0 {
workflow.Branches = []Branch{}
}
if len(workflow.Triggers) == 0 {
workflow.Triggers = []Trigger{}
}
if len(workflow.Errors) == 0 {
workflow.Errors = []string{}
}
if len(workflow.ExecutionVariables) > 0 {
log.Printf("[INFO] Found %d execution variable(s)", len(workflow.ExecutionVariables))
}
if len(workflow.WorkflowVariables) > 0 {
log.Printf("[INFO] Found %d workflow variable(s)", len(workflow.WorkflowVariables))
}
// FIXME - do actual checks ROFL
// FIXME - minor issues with e.g. hello world and self.console_logger
// Nodechecks
foundNodes := []string{}
for _, node := range allNodes {
for _, branch := range workflow.Branches {
//log.Println("branch")
//log.Println(node)
//log.Println(branch.DestinationID)
if node == branch.DestinationID || node == branch.SourceID {
foundNodes = append(foundNodes, node)
break
}
}
}
// FIXME - append all nodes (actions, triggers etc) to one single array here
//log.Printf("PRE VARIABLES")
if len(foundNodes) != len(allNodes) || len(workflow.Actions) <= 0 {
// This shit takes a few seconds lol
if !workflow.IsValid {
oldworkflow, err := getWorkflow(ctx, fileId)
if err != nil {
log.Printf("Workflow %s doesn't exist - oldworkflow.", fileId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Item already exists."}`))
return
}
oldworkflow.IsValid = false
err = setWorkflow(ctx, *oldworkflow, fileId)
if err != nil {
log.Printf("Failed saving workflow to database: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
}
// FIXME - more checks here - force reload of data or something
//if len(allNodes) == 0 {
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false, "reason": "Please insert a node"}`))
// return
//}
// Allowed with only a start node
//if len(allNodes) != 1 {
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false, "reason": "There are nodes with no branches"}`))
// return
//}
}
// FIXME - might be a sploit to run someone elses app if getAllWorkflowApps
// doesn't check sharing=true
// Have to do it like this to add the user's apps
//log.Println("Apps set starting")
//log.Printf("EXIT ON ERROR: %#v", workflow.Configuration.ExitOnError)
//workflowapps, apperr := getAllWorkflowApps(ctx, 500)
// Started getting the single apps, but if it's weird, this is faster
// 1. Check workflow.Start
// 2. Check if any node has "isStartnode"
if len(workflow.Actions) > 0 {
index := -1
for indexFound, action := range workflow.Actions {
//log.Println("Apps set done")
if workflow.Start == action.ID {
index = indexFound
}
}
if index >= 0 {
workflow.Actions[0].IsStartNode = true
} else {
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "You need to set a startnode."}`)))
return
}
}
allAuths, err := getAllWorkflowAppAuth(ctx, user.ActiveOrg.Id)
if userErr != nil {
log.Printf("Api authentication failed in get all apps: %s", userErr)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
// Check every app action and param to see whether they exist
//log.Printf("PRE ACTIONS 2")
newActions = []Action{}
for _, action := range workflow.Actions {
reservedApps := []string{
"0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e",
}
//log.Printf("%s Action execution var: %s", action.Label, action.ExecutionVariable.Name)
builtin := false
for _, id := range reservedApps {
if id == action.AppID {
builtin = true
break
}
}
// Check auth
// 1. Find the auth in question
// 2. Update the node and workflow info in the auth
// 3. Get the values in the auth and add them to the action values
if len(action.AuthenticationId) > 0 {
authFound := false
for _, auth := range allAuths {
if auth.Id == action.AuthenticationId {
authFound = true
// Updates the auth item itself IF necessary
go updateAppAuth(auth, workflow.ID, action.ID, true)
break
}
}
if !authFound {
log.Printf("App auth %s doesn't exist. Setting error", action.AuthenticationId)
workflow.Errors = append(workflow.Errors, fmt.Sprintf("App authentication for %s doesn't exist!", action.AppName))
workflow.IsValid = false
action.Errors = append(action.Errors, "App authentication doesn't exist")
action.IsValid = false
action.AuthenticationId = ""
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "App auth %s doesn't exist"}`, action.AuthenticationId)))
//return
}
}
if builtin {
newActions = append(newActions, action)
} else {
curapp := WorkflowApp{}
// FIXME - can this work with ONLY AppID?
for _, app := range workflowapps {
if app.ID == action.AppID {
curapp = app
break
}
// Has to NOT be generated
if app.Name == action.AppName && app.AppVersion == action.AppVersion {
curapp = app
break
}
}
// Check to see if the whole app is valid
if curapp.Name != action.AppName {
workflow.Errors = append(workflow.Errors, fmt.Sprintf("App %s doesn't exist", action.AppName))
action.Errors = append(action.Errors, "This app doesn't exist.")
action.IsValid = false
workflow.IsValid = false
// Append with errors
newActions = append(newActions, action)
log.Printf("App %s doesn't exist. Adding as error.", action.AppName)
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "App %s doesn't exist"}`, action.AppName)))
//return
} else {
// Check tosee if the appaction is valid
curappaction := WorkflowAppAction{}
for _, curAction := range curapp.Actions {
if action.Name == curAction.Name {
curappaction = curAction
break
}
}
// Check to see if the action is valid
if curappaction.Name != action.Name {
log.Printf("[ERROR] Action %s in app %s doesn't exist.", action.Name, curapp.Name)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Action %s in app %s doesn't exist"}`, action.Name, curapp.Name)))
return
}
// FIXME - check all parameters to see if they're valid
// Includes checking required fields
newParams := []WorkflowAppActionParameter{}
for _, param := range curappaction.Parameters {
found := false
// Handles check for parameter exists + value not empty in used fields
for _, actionParam := range action.Parameters {
if actionParam.Name == param.Name {
found = true
if actionParam.Value == "" && actionParam.Variant == "STATIC_VALUE" && actionParam.Required == true {
log.Printf("Appaction %s with required param '%s' is empty.", action.Name, param.Name)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Appaction %s with required param '%s' is empty."}`, action.Name, param.Name)))
return
}
if actionParam.Variant == "" {
actionParam.Variant = "STATIC_VALUE"
}
newParams = append(newParams, actionParam)
break
}
}
// Handles check for required params
if !found && param.Required {
log.Printf("Appaction %s with required param %s doesn't exist.", action.Name, param.Name)
action.Errors = append(action.Errors, "Parameter %s is required", param.Name)
//newActions = append(newActions, action)
//resp.WriteHeader(401)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Appaction %s with required param '%s' is empty."}`, action.Name, param.Name)))
//return
}
}
action.Parameters = newParams
newActions = append(newActions, action)
}
}
}
//log.Printf("PRE TRIGGERS")
//workflow.Actions = newActions
workflow.Actions = newActions
workflow.IsValid = true
@@ -2834,9 +2874,11 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
err = setWorkflow(ctx, workflow, fileId)
if err != nil {
log.Printf("Failed saving workflow to database: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
if workflow.PreviouslySaved {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
}
totalOldActions := len(tmpworkflow.Actions)
@@ -3235,7 +3277,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
}
// This one doesn't really matter.
log.Printf("[INFO] Running POST execution with body of length %d", len(string(body)))
log.Printf("[INFO] Running POST execution with body of length %d for workflow %s", len(string(body)), workflowExecution.Workflow.ID)
if len(body) >= 4 {
if body[0] == 34 && body[len(body)-1] == 34 {
@@ -3446,7 +3488,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
log.Printf("[INFO] No execution source (trigger) specified. Setting to default")
workflowExecution.ExecutionSource = "default"
} else {
log.Printf("[INFO] Execution source is %s for execution ID %s", workflowExecution.ExecutionSource, workflowExecution.ExecutionId)
log.Printf("[INFO] Execution source is %s for execution ID %s in workflow %s", workflowExecution.ExecutionSource, workflowExecution.ExecutionId, workflowExecution.Workflow.ID)
}
workflowExecution.ExecutionVariables = workflow.ExecutionVariables
@@ -3648,7 +3690,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
var allEnvs []Environment
if len(workflowExecution.ExecutionOrg) > 0 {
log.Printf("[INFO] Executing ORG: %s", workflowExecution.ExecutionOrg)
//log.Printf("[INFO] Executing ORG: %s", workflowExecution.ExecutionOrg)
allEnvironments, err := getEnvironments(ctx, workflowExecution.ExecutionOrg)
if err != nil {
@@ -3751,7 +3793,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
// FIXME - tmp name based on future companyname-companyId
// This leads to issues with overlaps. Should set limits and such instead
for _, environment := range environments {
log.Printf("[INFO] Execution: %s should execute onprem with execution environment \"%s\"", workflowExecution.ExecutionId, environment)
log.Printf("[INFO] Execution: %s should execute onprem with execution environment \"%s\". Workflow: %s", workflowExecution.ExecutionId, environment, workflowExecution.Workflow.ID)
executionRequest := ExecutionRequest{
ExecutionId: workflowExecution.ExecutionId,
+6 -1
View File
@@ -780,6 +780,11 @@ const AngularWorkflow = (props) => {
workflow.isValid = true
}
for (var key in workflow.errors) {
//console.log("Error: ", workflow.errors[key])
alert.info(workflow.errors[key])
}
setWorkflow(workflow)
}
//alert.success("Successfully saved workflow")
@@ -5131,7 +5136,7 @@ const AngularWorkflow = (props) => {
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
<div style={{flex: "10"}}>
<b>Login to either: </b>
<b>Login to either </b>
</div>
</div>
{outlookButton}
+60 -36
View File
@@ -12,6 +12,7 @@ import Dropzone from '../components/Dropzone';
import {Link} from 'react-router-dom';
import { useAlert } from "react-alert";
import ChipInput from 'material-ui-chip-input'
import uuid from "uuid"
const inputColor = "#383B40"
const surfaceColor = "#27292D"
@@ -92,31 +93,6 @@ const Workflows = (props) => {
}
})
// DEBUG HERE
const handleClickLogout = () => {
//console.log("Cookies: ", cookies)
//console.log("SHOULD LOG OUT")
//console.log(isLoggedIn)
// Don't really care about the logout
//fetch(globalUrl+"/api/v1/logout", {
// credentials: "include",
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
//})
//.then(() => {
// // Log out anyway
// removeCookie("session_token", {path: "/"})
// //window.location = "/login"
//})
//.catch(error => {
// console.log(error)
// removeCookie("session_token", {path: "/"})
//});
}
const deleteModal = deleteModalOpen ?
<Dialog
open={deleteModalOpen}
@@ -231,21 +207,21 @@ const Workflows = (props) => {
setSelectedExecution({})
setWorkflowExecutions([])
console.log(responseJson)
if (responseJson !== undefined) {
setWorkflows(responseJson)
setWorkflowDone(true)
} else {
if (isLoggedIn) {
alert.error("An error occurred while loading workflows")
} else {
handleClickLogout()
}
return
}
if (responseJson.length > 0){
setSelectedWorkflow(responseJson[0])
//setSelectedWorkflow(responseJson[0])
//getWorkflowExecution(responseJson[0].id)
}
})
@@ -417,12 +393,10 @@ const Workflows = (props) => {
const exportWorkflow = (data) => {
console.log("export")
let dataStr = JSON.stringify(data)
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
let exportFileDefaultName = data.name+'.json';
data["owner"] = ""
console.log(data)
if (data.triggers !== null && data.triggers !== undefined) {
for (var key in data.triggers) {
const trigger = data.triggers[key]
@@ -435,6 +409,22 @@ const Workflows = (props) => {
if (trigger.status == "running") {
trigger.status = "stopped"
}
const newId = uuid.v4()
for (var branchkey in data.branches) {
const branch = data.branches[branchkey]
if (branch.source_id === trigger.id) {
console.log("CHANGING SOURCE ID")
branch.source_id = newId
}
if (branch.destination_id === trigger.id) {
console.log("CHANGING DESTINATION ID")
branch.destination_id = newId
}
}
trigger.id = newId
}
}
@@ -445,9 +435,31 @@ const Workflows = (props) => {
for (var subkey in data.actions[key].parameters) {
const param = data.actions[key].parameters[subkey]
if (param.name.includes("key") || param.name.includes("user") || param.name.includes("pass") || param.name.includes("api") || param.name.includes("auth") || param.name.includes("secret")) {
param.value = ""
// FIXME: This may be a vuln if api-keys are generated that start with $
if (param.value.startsWith("$")) {
console.log("Skipping field, as it's referencing a variable")
} else {
param.value = ""
param.is_valid = false
}
}
}
const newId = uuid.v4()
for (var branchkey in data.branches) {
const branch = data.branches[branchkey]
if (branch.source_id === data.actions[key].id) {
console.log("CHANGING SOURCE ID IN ACTION")
branch.source_id = newId
}
if (branch.destination_id === data.actions[key].id) {
console.log("CHANGING DESTINATION ID IN ACTION")
branch.destination_id = newId
}
}
data.actions[key].id = newId
}
}
@@ -456,6 +468,7 @@ const Workflows = (props) => {
const param = data.workflow_variables[key]
if (param.name.includes("key") || param.name.includes("user") || param.name.includes("pass") || param.name.includes("api") || param.name.includes("auth") || param.name.includes("secret")) {
param.value = ""
param.is_valid = false
}
}
}
@@ -465,9 +478,16 @@ const Workflows = (props) => {
data["org"] = []
data["org_id"] = ""
data["execution_org"] = {}
// These are backwards.. True = saved before. Very confuse.
data["previously_saved"] = false
data["first_save"] = false
data.execution_org = {"id": ""}
console.log(data)
let dataStr = JSON.stringify(data)
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
let linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
@@ -477,9 +497,9 @@ const Workflows = (props) => {
const copyWorkflow = (data) => {
data = JSON.parse(JSON.stringify(data))
alert.success("Copying workflow "+data.name)
console.log("data: ", data)
data.id = ""
data.name = data.name+"_copy"
console.log("COPIED DATA: ", data)
//return
fetch(globalUrl+"/api/v1/workflows", {
@@ -503,7 +523,7 @@ const Workflows = (props) => {
})
.catch(error => {
alert.error(error.toString())
});
})
}
@@ -571,6 +591,7 @@ const Workflows = (props) => {
}
const imgSize = 25
//console.log("TOP INFO: ", data)
return (
<Paper square style={paperAppStyle} onClick={(e) => {
}}>
@@ -580,7 +601,7 @@ const Workflows = (props) => {
<Grid item style={{flex: 1, display: "flex"}}>
<div style={{flex: "10",}} onClick={() => {
if (selectedWorkflow.id !== data.id) {
setSelectedWorkflow(data)
//setSelectedWorkflow(data)
//getWorkflowExecution(data.id)
}
}}>
@@ -637,7 +658,7 @@ const Workflows = (props) => {
</Grid>
<div style={{display: "flex", flex: 1}} onClick={() => {
if (selectedWorkflow.id !== data.id) {
setSelectedWorkflow(data)
//setSelectedWorkflow(data)
//getWorkflowExecution(data.id)
}
}}>
@@ -1064,6 +1085,9 @@ const Workflows = (props) => {
if (response !== undefined) {
// SET THE FULL THING
data.id = response.id
data.first_save = false
data.previously_saved = false
data.is_valid = false
// Actually create it
const ret = setNewWorkflow(data.name, data.description, data.tags, data, false)