diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index d25297eb..1492305a 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -308,7 +308,18 @@ class AppBase: print("DATA: %s\n" % data) return parse_wrapper(data) + # Looks for parantheses to grab special cases within a string, e.g: + # int(1) lower(HELLO) or length(what's the length) + # FIXME: + # There is an issue in here where it returns data wrong. Example: + # Authorization=Bearer authkey + # = + # Authorization=Bearer authkey + # ^ Double space. def parse_wrapper_start(data): + if "(" not in data or ")" not in data: + return data + newdata = [] newstring = "" record = True @@ -337,7 +348,7 @@ class AppBase: if len(newstring) > 0: newdata.append(newstring) - #print(newdata) + print("Newdata: ", newdata) parsedlist = [] non_string = False for item in newdata: @@ -348,17 +359,20 @@ class AppBase: parsedlist.append(ret) if len(parsedlist) > 0 and not non_string: + print("Returning parsed list: ", parsedlist) return " ".join(parsedlist) elif len(parsedlist) == 1 and non_string: return parsedlist[0] else: - #print("Casting back to string because multi: ", parsedlist) + print("Casting back to string because multi: ", parsedlist) newlist = [] for item in parsedlist: try: newlist.append(str(item)) except ValueError: newlist.append("parsing_error") + + # Does this create the issue? return " ".join(newlist) # Parses JSON loops and such down to the item you're looking for @@ -989,6 +1003,7 @@ class AppBase: print("Normal parsing (not looping) with data %s" % value) value = parse_wrapper_start(value) + print("POST data value: %s" % value) params[parameter["name"]] = value multi_parameters[parameter["name"]] = value @@ -1019,8 +1034,9 @@ class AppBase: #for i in range(calltimes): if not multiexecution: print("APP_SDK DONE: Starting NORMAL execution of function") + print("Running with params %s" % params) newres = await func(**params) - #print("NEWRES: ", newres) + print("Return from execution: %s" % newres) if isinstance(newres, str): result += newres else: @@ -1101,6 +1117,7 @@ class AppBase: print("Running with params %s" % baseparams) ret = await func(**baseparams) + print("Return from execution: %s" % ret) if isinstance(ret, dict) or isinstance(ret, list): results.append(ret) json_object = True diff --git a/backend/go-app/main.go b/backend/go-app/main.go index bd0f2153..e88e8a9e 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -390,6 +390,7 @@ type Hook struct { Status string `json:"status" datastore:"status"` Workflows []string `json:"workflows" datastore:"workflows"` Running bool `json:"running" datastore:"running"` + OrgId string `json:"org_id" datastore:"org_id"` } func createFileFromFile(ctx context.Context, bucket *storage.BucketHandle, remotePath, localPath string) error { @@ -689,47 +690,14 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U authorization = authorizationArr[0] } _ = authorization - - //if item, err := memcache.Get(ctx, authorization); err == memcache.ErrCacheMiss { - // // Doesn't exist :( - // log.Printf("Couldn't find %s in cache!", authorization) - // return User{}, err - //} else if err != nil { - // log.Printf("Error getting item: %v", err) - // return User{}, err - //} else { - // log.Printf("%#v", item.Value) - // var Userdata User - - // log.Printf("Deleting key %s", authorization) - // memcache.Delete(ctx, authorization) - // err = json.Unmarshal(item.Value, &Userdata) - // if err == nil { - // return Userdata, nil - // } - - // return User{}, err - //} } c, err := request.Cookie("session_token") if err == nil { - //if item, err := memcache.Get(ctx, c.Value); err == memcache.ErrCacheMiss { - // // Not in cache - //} else if err != nil { - // log.Printf("Error getting item: %v", err) - //} else { - // var Userdata User - // err = json.Unmarshal(item.Value, &Userdata) - // if err == nil { - // return Userdata, nil - // } - //} - sessionToken := c.Value session, err := getSession(ctx, sessionToken) if err != nil { - log.Printf("Session %s doesn't exist (api auth): %s", sessionToken, err) + log.Printf("Session %s doesn't exist (session auth): %s", sessionToken, err) return User{}, err } @@ -865,7 +833,7 @@ func deleteUser(resp http.ResponseWriter, request *http.Request) { return } - user, userErr := handleApiAuthentication(resp, request) + userInfo, userErr := handleApiAuthentication(resp, request) if userErr != nil { log.Printf("Api authentication failed in edit workflow: %s", userErr) resp.WriteHeader(401) @@ -873,8 +841,8 @@ func deleteUser(resp http.ResponseWriter, request *http.Request) { return } - if user.Role != "admin" { - log.Printf("Wrong user (%s) when deleting - must be admin", user.Username) + if userInfo.Role != "admin" { + log.Printf("Wrong user (%s) when deleting - must be admin", userInfo.Username) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Must be admin"}`)) return @@ -892,46 +860,57 @@ func deleteUser(resp http.ResponseWriter, request *http.Request) { userId = location[4] } - if userId == user.Id { + if userId == userInfo.Id { resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Can't deactivate yourself"}`)) return } ctx := context.Background() - q := datastore.NewQuery("Users").Filter("id =", userId) - var users []User - _, err := dbclient.GetAll(ctx, q, &users) + foundUser, err := getUser(ctx, userId) if err != nil { - log.Printf("Error getting users apikey (deleteuser): %s", err) + log.Printf("Can't find user %s (delete user): %s", userId, err) resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "Failed getting users for verification"}`)) + resp.Write([]byte(fmt.Sprintf(`{"success": false}`))) return } - if len(users) != 1 { - log.Printf("Found too many users!") - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Backend error: too many or too few users with id %s: %d"}`, userId, len(users)))) + orgFound := false + if userInfo.ActiveOrg.Id == foundUser.ActiveOrg.Id { + orgFound = true + } else { + log.Printf("FoundUser: %#v", foundUser.Orgs) + for _, item := range foundUser.Orgs { + if item == userInfo.ActiveOrg.Id { + orgFound = true + break + } + } + } + + if !orgFound { + log.Printf("User %s is admin, but can't delete users outside their own org.", userInfo.Id) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't change users outside your org."}`))) return } // Invert. No user deletion. - if users[0].Active { - users[0].Active = false + if foundUser.Active { + foundUser.Active = false } else { - users[0].Active = true + foundUser.Active = true } - err = setUser(ctx, &users[0]) + err = setUser(ctx, foundUser) if err != nil { - log.Printf("Failed swapping active for user %s (%s)", users[0].Username, users[0].Id) + log.Printf("Failed swapping active for user %s (%s)", foundUser, foundUser.Username, foundUser.Id) resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": true"}`))) + resp.Write([]byte(fmt.Sprintf(`{"success": false}`))) return } - log.Printf("Successfully inverted %s", users[0].Username) + log.Printf("Successfully inverted %s", foundUser.Username) resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) @@ -1091,7 +1070,7 @@ func handleSetEnvironments(resp http.ResponseWriter, request *http.Request) { } for _, item := range newEnvironments { - if item.OrgId == "" { + if item.OrgId != user.ActiveOrg.Id { item.OrgId = user.ActiveOrg.Id } @@ -1327,51 +1306,53 @@ func handleLogout(resp http.ResponseWriter, request *http.Request) { return } - // Check cookie - c, err := request.Cookie("session_token") + userInfo, err := handleApiAuthentication(resp, request) if err != nil { - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } else { - log.Printf("Session cookie is set!") - } - - var Userdata User - ctx := context.Background() - //item, err := memcache.Get(ctx, c.Value) - sessionToken := "" - //// Memcache handling for logout - //if err == nil { - // err = json.Unmarshal(item.Value, &Userdata) - // if err != nil { - // log.Printf("Failed unmarshaling: %s", err) - // resp.WriteHeader(401) - // resp.Write([]byte(fmt.Sprintf(`{"success": false}`))) - // return - // } - - // sessionToken = Userdata.Session - //} else { - // // Validate with User - sessionToken = c.Value - session, err := getSession(ctx, sessionToken) - if err != nil { - log.Printf("Session %s doesn't exist (logout): %s", session.Session, err) + log.Printf("Api authentication failed in handleLogout: %s", err) resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) + resp.Write([]byte(`{"success": false}`)) return } + ctx := context.Background() + session, err := getSession(ctx, userInfo.Session) + if err != nil { + log.Printf("Session %#v doesn't exist: %s", session, err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "No session"}`)) + return + } + + // Check cookie + //c, err := request.Cookie("session_token") + //if err != nil { + // resp.WriteHeader(200) + // resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + // return + //} else { + // log.Printf("Session cookie is set to %s!", c.Value) + //} + + //var Userdata User + //ctx := context.Background() + //sessionToken = c.Value + //session, err := getSession(ctx, sessionToken) + //if err != nil { + // log.Printf("Session %s doesn't exist (logout): %s", sessionToken, err) + // resp.WriteHeader(401) + // resp.Write([]byte(`{"success": false, "reason": "Couldn't find your session"}`)) + // return + //} + // Get session first // Should basically never happen - _, err = getUser(ctx, session.Id) - if err != nil { - log.Printf("Username %s doesn't exist (logout): %s", session.Username, err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) - return - } + //_, err = getUser(ctx, session.Id) + //if err != nil { + // log.Printf("Username %s doesn't exist (logout): %s", session.Username, err) + // resp.WriteHeader(401) + // resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) + // return + //} // Userdata = *tmpdata //} @@ -1379,7 +1360,7 @@ func handleLogout(resp http.ResponseWriter, request *http.Request) { // FIXME // Session might delete someone elses here? // No need to think about before possible scale..? - err = SetSession(ctx, Userdata, "") + err = SetSession(ctx, userInfo, "") if err != nil { log.Printf("Error removing session for: %s", err) resp.WriteHeader(401) @@ -1387,16 +1368,16 @@ func handleLogout(resp http.ResponseWriter, request *http.Request) { return } - err = DeleteKey(ctx, "sessions", sessionToken) + err = DeleteKey(ctx, "sessions", userInfo.Session) if err != nil { - log.Printf("Error deleting key %s for %s: %s", c.Value, Userdata.Username, err) + log.Printf("Error deleting key %s for %s: %s", userInfo.Session, userInfo.Username, err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) return } - Userdata.Session = "" - err = setUser(ctx, &Userdata) + userInfo.Session = "" + err = setUser(ctx, &userInfo) if err != nil { log.Printf("Failed updating user: %s", err) resp.WriteHeader(401) @@ -1405,10 +1386,10 @@ func handleLogout(resp http.ResponseWriter, request *http.Request) { } //memcache.Delete(request.Context(), sessionToken) + //http.SetCookie(resp, c) resp.WriteHeader(200) resp.Write([]byte(`{"success": false, "reason": "Successfully logged out"}`)) - http.SetCookie(resp, c) } func generateApikey(ctx context.Context, userInfo User) (User, error) { @@ -1471,6 +1452,8 @@ func handleUpdateUser(resp http.ResponseWriter, request *http.Request) { return } + // Should this role reflect the users' org access? + // When you change org -> change user role if userInfo.Role != "admin" { log.Printf("%s tried to update user %s", userInfo.Username, t.UserId) resp.WriteHeader(401) @@ -1486,6 +1469,21 @@ func handleUpdateUser(resp http.ResponseWriter, request *http.Request) { return } + orgFound := false + for _, item := range foundUser.Orgs { + if item == userInfo.ActiveOrg.Id { + orgFound = true + break + } + } + + if !orgFound { + log.Printf("User %s is admin, but can't edit users outside their own org.", userInfo.Id) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't change users outside your org."}`))) + return + } + if t.Role != "admin" && t.Role != "user" { log.Printf("%s tried and failed to update user %s", userInfo.Username, t.UserId) resp.WriteHeader(401) @@ -1657,13 +1655,13 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() - session, err := getSession(ctx, userInfo.Session) - if err != nil { - log.Printf("Session %#v doesn't exist: %s", session, err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "No session"}`)) - return - } + //session, err := getSession(ctx, userInfo.Session) + //if err != nil { + // log.Printf("Session %#v doesn't exist: %s", session, err) + // resp.WriteHeader(401) + // resp.Write([]byte(`{"success": false, "reason": "No session"}`)) + // return + //} // This is a long check to see if an inactive admin can access the site parsedAdmin := "false" @@ -1726,12 +1724,12 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { } //log.Printf("%s %s", session.Session, UserInfo.Session) - if session.Session != userInfo.Session { - log.Printf("Session %s is not the same as %s for %s. %s", userInfo.Session, session.Session, userInfo.Username, err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - return - } + //if session.Session != userInfo.Session { + // log.Printf("Session %s is not the same as %s for %s. %s", userInfo.Session, session.Session, userInfo.Username, err) + // resp.WriteHeader(401) + // resp.Write([]byte(`{"success": false, "reason": ""}`)) + // return + //} expiration := time.Now().Add(3600 * time.Second) http.SetCookie(resp, &http.Cookie{ @@ -2015,7 +2013,7 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { return } - user, err := handleApiAuthentication(resp, request) + userInfo, err := handleApiAuthentication(resp, request) if err != nil { log.Printf("Api authentication failed in set new workflowhandler: %s", err) resp.WriteHeader(401) @@ -2024,15 +2022,15 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { } curUserFound := false - if t.Username != user.Username && user.Role != "admin" { + if t.Username != userInfo.Username && userInfo.Role != "admin" { resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Admin required to change others' passwords"}`)) return - } else if t.Username == user.Username { + } else if t.Username == userInfo.Username { curUserFound = true } - if user.Role != "admin" { + if userInfo.Role != "admin" { if t.Newpassword != t.Newpassword2 { err := "Passwords don't match" resp.WriteHeader(401) @@ -2046,6 +2044,8 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) return } + } else { + // Check ORG HERE? } // Current password @@ -2058,6 +2058,7 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() + foundUser := User{} if !curUserFound { log.Printf("Have to find a different user") q := datastore.NewQuery("Users").Filter("Username =", strings.ToLower(t.Username)) @@ -2077,13 +2078,32 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { return } - user = users[0] + foundUser = users[0] + orgFound := false + if userInfo.ActiveOrg.Id == foundUser.ActiveOrg.Id { + orgFound = true + } else { + log.Printf("FoundUser: %#v", foundUser.Orgs) + for _, item := range foundUser.Orgs { + if item == userInfo.ActiveOrg.Id { + orgFound = true + break + } + } + } + + if !orgFound { + log.Printf("User %s is admin, but can't change user's passowrd outside their own org.", userInfo.Id) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't change users outside your org."}`))) + return + } } else { // Admins can re-generate others' passwords as well. - if user.Role != "admin" { - err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(t.Newpassword)) + if userInfo.Role != "admin" { + err = bcrypt.CompareHashAndPassword([]byte(userInfo.Password), []byte(t.Newpassword)) if err != nil { - log.Printf("Bad password for %s: %s", user.Username, err) + log.Printf("Bad password for %s: %s", userInfo.Username, err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) return @@ -2091,18 +2111,25 @@ func handlePasswordChange(resp http.ResponseWriter, request *http.Request) { } } + if len(foundUser.Id) == 0 { + log.Printf("Something went wrong in password reset", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false}`)) + return + } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(t.Newpassword), 8) if err != nil { - log.Printf("New password failure for %s: %s", user.Username, err) + log.Printf("New password failure for %s: %s", userInfo.Username, err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) return } - user.Password = string(hashedPassword) - err = setUser(ctx, &user) + userInfo.Password = string(hashedPassword) + err = setUser(ctx, &foundUser) if err != nil { - log.Printf("Error fixing password for user %s: %s", user.Username, err) + log.Printf("Error fixing password for user %s: %s", userInfo.Username, err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Username and/or password is incorrect"}`)) return @@ -2341,17 +2368,15 @@ func handleGetUsers(resp http.ResponseWriter, request *http.Request) { // FIXME: Check by org. ctx := context.Background() - var users []User - q := datastore.NewQuery("Users") - _, err = dbclient.GetAll(ctx, q, &users) + org, err := getOrg(ctx, user.ActiveOrg.Id) if err != nil { resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "Can't get users"}`)) + resp.Write([]byte(`{"success": false, "reason": "Failed getting org users"}`)) return } newUsers := []User{} - for _, item := range users { + for _, item := range org.Users { if len(item.Username) == 0 { continue } @@ -2458,19 +2483,10 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) { return } - log.Printf("%s SUCCESSFULLY LOGGED IN with session %s", data.Username, Userdata.Session) - //if !Userdata.Verified { - // log.Printf("User %s is not verified", data.Username) - // resp.WriteHeader(403) - // resp.Write([]byte(`{"success": false, "reason": "Successful login, but your email address isn't verified. Check your mailbox."}`)) - // return - //} - - loginData := `{"success": true}` - // FIXME - have timeout here + loginData := `{"success": true}` if len(Userdata.Session) != 0 { - //log.Println("Nonexisting session") + log.Println("User session exists - resetting") expiration := time.Now().Add(3600 * time.Second) http.SetCookie(resp, &http.Cookie{ @@ -2490,20 +2506,36 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(200) resp.Write([]byte(loginData)) return + } else { + log.Printf("User session is empty - create one!") + + sessionToken := uuid.NewV4().String() + expiration := time.Now().Add(3600 * time.Second) + http.SetCookie(resp, &http.Cookie{ + Name: "session_token", + Value: sessionToken, + Expires: expiration, + }) + + // ADD TO DATABASE + err = SetSession(ctx, Userdata, sessionToken) + if err != nil { + log.Printf("Error adding session to database: %s", err) + } + + Userdata.Session = sessionToken + err = setUser(ctx, &Userdata) + if err != nil { + log.Printf("Failed updating user when setting session: %s", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false}`)) + return + } + + loginData = fmt.Sprintf(`{"success": true, "cookies": [{"key": "session_token", "value": "%s", "expiration": %d}]}`, sessionToken, expiration.Unix()) } - sessionToken := uuid.NewV4() - http.SetCookie(resp, &http.Cookie{ - Name: "session_token", - Value: sessionToken.String(), - Expires: time.Now().Add(3600 * time.Second), - }) - - // ADD TO DATABASE - err = SetSession(ctx, Userdata, sessionToken.String()) - if err != nil { - log.Printf("Error adding session to database: %s", err) - } + log.Printf("%s SUCCESSFULLY LOGGED IN with session %s", data.Username, Userdata.Session) resp.WriteHeader(200) resp.Write([]byte(loginData)) @@ -2685,8 +2717,61 @@ func setEnvironment(ctx context.Context, data *Environment) error { return nil } +func fixUserOrg(ctx context.Context, user *User) *User { + found := false + for _, id := range user.Orgs { + if user.ActiveOrg.Id == id { + found = true + break + } + } + + if !found { + user.Orgs = append(user.Orgs, user.ActiveOrg.Id) + } + + log.Printf("Updating %d orgs for user %s", len(user.Orgs), user.Id) + // Might be vulnerable to timing attacks. + for _, orgId := range user.Orgs { + if len(orgId) == 0 { + continue + } + + org, err := getOrg(ctx, orgId) + if err != nil { + log.Printf("Error getting org %s", orgId) + continue + } + + orgIndex := 0 + userFound := false + for index, orgUser := range org.Users { + if orgUser.Id == user.Id { + orgIndex = index + userFound = true + break + } + } + + if userFound { + org.Users[orgIndex] = *user + } else { + org.Users = append(org.Users, *user) + } + + err = setOrg(ctx, *org, orgId) + if err != nil { + log.Printf("Failed setting org %s", orgId) + } + } + + return user +} + // ListBooks returns a list of books, ordered by title. func setUser(ctx context.Context, data *User) error { + data = fixUserOrg(ctx, data) + // clear session_token and API_token for user k := datastore.NameKey("Users", data.Id, nil) if _, err := dbclient.Put(ctx, k, data); err != nil { @@ -3481,6 +3566,7 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) { }, }, Running: false, + OrgId: user.ActiveOrg.Id, } hook.Status = "running" @@ -7027,6 +7113,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { } // FIXME: Check if user is admin of this org + log.Printf("Checking org %s", org.Name) userFound := false admin := false for _, inneruser := range org.Users { @@ -7061,7 +7148,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { // FIXME: Path client := &http.Client{} - apiPath := "/api/v1/cloud/sync" + apiPath := "/api/v1/cloud/sync/setup" if tmpData.Disable { if !org.CloudSync { log.Printf("Org %s isn't syncing. Can't stop.", org.Id) @@ -7072,9 +7159,9 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { log.Printf("Should disable sync for org %s", org.Id) apiPath := "/api/v1/cloud/sync/stop" - syncUrl = fmt.Sprintf("%s%s", syncUrl, apiPath) + syncPath := fmt.Sprintf("%s%s", syncUrl, apiPath) - err = handleStopCloudSync(syncUrl, *org) + err = handleStopCloudSync(syncPath, *org) if err != nil { resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) @@ -7396,9 +7483,9 @@ func initHandlers() { r.HandleFunc("/api/v1/users/logout", handleLogout).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/users/getusers", handleGetUsers).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getsettings", handleSettings).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/users/getusers", handleGetUsers).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/updateuser", handleUpdateUser).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/users/{user}", deleteUser).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/users/passwordchange", handlePasswordChange).Methods("POST", "OPTIONS") @@ -7413,10 +7500,10 @@ func initHandlers() { r.HandleFunc("/api/v1/getinfo", handleInfo).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/getsettings", handleSettings).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/generateapikey", handleApiGeneration).Methods("GET", "POST", "OPTIONS") + r.HandleFunc("/api/v1/passwordchange", handlePasswordChange).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/getenvironments", handleGetEnvironments).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/setenvironments", handleSetEnvironments).Methods("PUT", "OPTIONS") - r.HandleFunc("/api/v1/passwordchange", handlePasswordChange).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/docs", getDocList).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/docs/{key}", getDocs).Methods("GET", "OPTIONS") @@ -7433,6 +7520,7 @@ func initHandlers() { r.HandleFunc("/api/v1/workflows/queue/confirm", handleGetWorkflowqueueConfirm).Methods("POST") // App specific + // From here down isnt checked for org specific r.HandleFunc("/api/v1/apps/run_hotload", handleAppHotloadRequest).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps/get_existing", loadSpecificApps).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/download_remote", loadSpecificApps).Methods("POST", "OPTIONS") @@ -7472,7 +7560,6 @@ func initHandlers() { 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") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 0f52aabd..c0c643d3 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -105,6 +105,7 @@ type AppAuthenticationStorage struct { Usage []AuthenticationUsage `json:"usage" datastore:"usage"` WorkflowCount int64 `json:"workflow_count" datastore:"workflow_count"` NodeCount int64 `json:"node_count" datastore:"node_count"` + OrgId string `json:"org_id" datastore:"org_id"` } type AuthenticationUsage struct { @@ -197,6 +198,7 @@ type WorkflowAppAction struct { } // FIXME: Generate a callback authentication ID? +// FIXME: Add org check .. type WorkflowExecution struct { Type string `json:"type" datastore:"type"` Status string `json:"status" datastore:"status"` @@ -221,6 +223,7 @@ type WorkflowExecution struct { Name string `json:"name" datastore:"name"` Value string `json:"value" datastore:"value,noindex"` } `json:"execution_variables,omitempty" datastore:"execution_variables,omitempty"` + OrgId string `json:"org_id" datastore:"org_id"` } // This is for the nodes in a workflow, NOT the app action itself. @@ -303,6 +306,7 @@ type Schedule struct { Frequency string `json:"frequency" datastore:"frequency"` ExecutionArgument string `json:"execution_argument" datastore:"execution_argument,noindex"` Id string `json:"id" datastore:"id"` + OrgId string `json:"org_id" datastore:"org_id"` } type Workflow struct { @@ -1047,6 +1051,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { extraInputs := 0 for _, result := range workflowExecution.Results { if result.Action.Name == "User Input" && result.Action.AppName == "User Input" { + log.Printf("Found User Input node - prepare cloud?") extraInputs += 1 } } @@ -1904,7 +1909,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) { } } - allAuths, err := getAllWorkflowAppAuth(ctx) + allAuths, err := getAllWorkflowAppAuth(ctx, user.ActiveOrg.Id) if userErr != nil { log.Printf("Api authentication failed in get all apps: %s", userErr) resp.WriteHeader(401) @@ -2575,7 +2580,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf // FIXME: Authentication parameters if len(action.AuthenticationId) > 0 { if len(allAuths) == 0 { - allAuths, err = getAllWorkflowAppAuth(ctx) + allAuths, err = getAllWorkflowAppAuth(ctx, workflow.ExecutingOrg.Id) if err != nil { log.Printf("Api authentication failed in get all app auth: %s", err) return WorkflowExecution{}, fmt.Sprintf("Api authentication failed in get all app auth: %s", err), err @@ -2786,10 +2791,18 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf return WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet") } + // What it needs to know: + // 1. Parameters if len(workflowExecution.Workflow.Actions) == 1 { log.Printf("Should execute directly with cloud instead of worker because only one action") - cloudExecuteAction(workflowExecution.ExecutionId, workflowExecution.Workflow.Actions[0], workflowExecution.ExecutionOrg) - return WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet") + + //cloudExecuteAction(workflowExecution.ExecutionId, workflowExecution.Workflow.Actions[0], workflowExecution.ExecutionOrg, workflowExecution.Workflow.ID) + cloudExecuteAction(workflowExecution) + return WorkflowExecution{}, "Cloud not implemented yet (1)", errors.New("Cloud not implemented yet") + } else { + // If it's here, it should be controlled by Worker. + // If worker, should this backend be a proxy? I think so. + return WorkflowExecution{}, "Cloud not implemented yet (2)", errors.New("Cloud not implemented yet") } } @@ -2802,22 +2815,30 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf } // This updates stuff locally from remote executions -func cloudExecuteAction(workflowExecutionId string, action Action, orgId string) error { - log.Printf("Executing action: %#v in execution ID %s", action, workflowExecutionId) +func cloudExecuteAction(execution WorkflowExecution) error { ctx := context.Background() - org, err := getOrg(ctx, orgId) + org, err := getOrg(ctx, execution.ExecutionOrg) if err != nil { return err } type ExecutionStruct struct { - ID string `json:"id"` - Action Action `json:"action"` + ExecutionId string `json:"execution_id" datastore:"execution_id"` + Action Action `json:"action" datastore:"action"` + Authorization string `json:"authorization" datastore:"authorization"` + Results []ActionResult `json:"results" datastore:"results,noindex"` + ExecutionArgument string `json:"execution_argument" datastore:"execution_argument,noindex"` + WorkflowId string `json:"workflow_id" datastore:"workflow_id"` + ExecutionSource string `json:"execution_source" datastore:"execution_source"` } + data := ExecutionStruct{ - ID: workflowExecutionId, - Action: action, + ExecutionId: execution.ExecutionId, + WorkflowId: execution.Workflow.ID, + Action: execution.Workflow.Actions[0], + Authorization: execution.Authorization, } + log.Printf("Executing action: %#v in execution ID %s", data.Action, data.ExecutionId) b, err := json.Marshal(data) if err != nil { @@ -3899,7 +3920,7 @@ func getAppAuthentication(resp http.ResponseWriter, request *http.Request) { return } - _, userErr := handleApiAuthentication(resp, request) + user, userErr := handleApiAuthentication(resp, request) if userErr != nil { log.Printf("Api authentication failed in get all apps: %s", userErr) resp.WriteHeader(401) @@ -3915,7 +3936,7 @@ func getAppAuthentication(resp http.ResponseWriter, request *http.Request) { // return //} ctx := context.Background() - allAuths, err := getAllWorkflowAppAuth(ctx) + allAuths, err := getAllWorkflowAppAuth(ctx, user.ActiveOrg.Id) if err != nil { log.Printf("Api authentication failed in get all app auth: %s", err) resp.WriteHeader(401) @@ -5469,9 +5490,9 @@ func getAllWorkflowApps(ctx context.Context) ([]WorkflowApp, error) { return allworkflowapps, nil } -func getAllWorkflowAppAuth(ctx context.Context) ([]AppAuthenticationStorage, error) { +func getAllWorkflowAppAuth(ctx context.Context, OrgId string) ([]AppAuthenticationStorage, error) { var allworkflowapps []AppAuthenticationStorage - q := datastore.NewQuery("workflowappauth") + q := datastore.NewQuery("workflowappauth").Filter("org_id = ", OrgId) _, err := dbclient.GetAll(ctx, q, &allworkflowapps) if err != nil { diff --git a/docker-compose.yml b/docker-compose.yml index e01176d0..37e61122 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: frikky/shuffle:frontend container_name: shuffle-frontend hostname: shuffle-frontend diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 053f0ab5..446ab4df 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react'; import { Route } from 'react-router'; import { BrowserRouter } from 'react-router-dom'; import { CookiesProvider } from 'react-cookie'; -import { useCookies } from 'react-cookie'; +import { removeCookies, useCookies } from 'react-cookie'; import EditSchedule from "./views/EditSchedule"; import Schedules from "./views/Schedules"; @@ -93,23 +93,24 @@ const App = (message, props) => { 'Content-Type': 'application/json', }, }) - .then(response => response.json()) - .then(responseJson => { - if (responseJson.success === true) { - //console.log(responseJson.success) - setUserData(responseJson) - setIsLoggedIn(true) + .then(response => response.json()) + .then(responseJson => { + if (responseJson.success === true) { + //console.log(responseJson.success) + setUserData(responseJson) + setIsLoggedIn(true) + console.log("Cookies: ", cookies) - // Updating cookie every request - for (var key in responseJson["cookies"]) { - setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) - } + // Updating cookie every request + for (var key in responseJson["cookies"]) { + setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) } - setIsLoaded(true) - }) - .catch(error => { - setIsLoaded(true) - }); + } + setIsLoaded(true) + }) + .catch(error => { + setIsLoaded(true) + }); } // Dumb for content load (per now), but good for making the site not suddenly reload parts (ajax thingies) @@ -124,7 +125,7 @@ const App = (message, props) => { } /> :
-
+
} /> } /> } /> @@ -140,7 +141,7 @@ const App = (message, props) => { } /> } /> } /> - } /> + } /> } /> } /> { window.location.pathname = "/docs/about" }} /> diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 4bc78430..11dcf59c 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -19,7 +19,7 @@ const hoverColor = "#f85a3e" const hoverOutColor = "#e8eaf6" const Header = props => { - const { globalUrl, isLoggedIn, removeCookie, homePage, isLoaded, userdata } = props; + const { globalUrl, isLoggedIn, removeCookie, homePage, isLoaded, userdata, cookies } = props; const theme = useTheme(); const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); @@ -35,26 +35,31 @@ const Header = props => { // DEBUG HERE const handleClickLogout = () => { - console.log("SHOULD LOG OUT") - console.log(isLoggedIn) - + console.log("COOKIES: ", cookies, "Remover: ", removeCookie) // Don't really care about the logout - fetch(globalUrl+"/api/v1/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.pathname = "/" - }) + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(() => { + // Log out anyway + //cookies.remove("session_token") + //window.location.pathname = "/" + console.log("Should've logged out") + removeCookie("session_token", {path: "/"}) + removeCookie("session_token", {path: "/workflows"}) + window.location.reload() + }) .catch(error => { - console.log(error) - }); - } + console.log("Error in logout: ", error) + removeCookie("session_token", {path: "/"}) + window.location.reload() + //removeCookie("session_token", {path: "/"}) + }) + } // Rofl this is weird const handleDocsHover = () => { diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 9978dd97..34141a0e 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1136,9 +1136,9 @@ const Admin = (props) => { }} > Edit user - + - + ) })} diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 8a19fbed..fa480e33 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -189,6 +189,8 @@ const AngularWorkflow = (props) => { const [workflowExecutions, setWorkflowExecutions] = React.useState([]); const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0) + const cloudSyncEnabled = props.userdata !== undefined && props.userdata.active_org !== null && props.userdata.active_org !== undefined ? props.userdata.active_org.cloud_sync === true : false + const unloadText = 'Are you sure you want to leave without saving (CTRL+S)?' useBeforeunload(() => { if (!lastSaved) { @@ -1896,7 +1898,6 @@ const AngularWorkflow = (props) => { ) } - const syncEnabled = props.userdata !== undefined && props.userdata.selected_org !== null && props.userdata.selected_org !== undefined ? props.userdata.selected_org.cloud_sync === true : false const triggers = [{ "name": "Webhook", "type": "TRIGGER", @@ -1918,7 +1919,7 @@ const AngularWorkflow = (props) => { "description": "Wait for user input", "trigger_type": "USERINPUT", "errors": null, - "is_valid": syncEnabled, + "is_valid": cloudSyncEnabled, "label": "User input", "environment": environments[defaultEnvironmentIndex] === undefined ? {} : environments[defaultEnvironmentIndex].Name, "long_description": "Take user input to continue execution", @@ -1943,7 +1944,7 @@ const AngularWorkflow = (props) => { "description": "Add your email provider", "trigger_type": "EMAIL", "errors": null, - "is_valid": syncEnabled, + "is_valid": cloudSyncEnabled, "label": "Email", "environment": environments[defaultEnvironmentIndex] === undefined ? {} : environments[defaultEnvironmentIndex].Name, "large_image": 'data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAytodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Nzg4QTJBMjVEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Nzg4QTJBMjZEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODhBMkEyM0QwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3ODhBMkEyNEQwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAgICAwICAgMDAwMEBgQEBAQECAYGBQYJCAoKCQgJCQoM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', @@ -1963,7 +1964,7 @@ const AngularWorkflow = (props) => { return (
- {triggers.map(trigger => { + {triggers.map((trigger, index) => { var imageline = trigger.large_image.length === 0 ? : @@ -1972,6 +1973,7 @@ const AngularWorkflow = (props) => { const color = trigger.is_valid ? "green" : "orange" return( {handleTriggerDrag(e, trigger)}} onStop={(e) => {handleDragStop(e)}} dragging={false} @@ -4896,28 +4898,29 @@ const AngularWorkflow = (props) => { { - setTriggerOptionsWrapper("email") - }} - color="primary" - value="email" - /> - } + { + setTriggerOptionsWrapper("email") + }} + color="primary" + value="email" + /> + } label={
Email
} /> { - setTriggerOptionsWrapper("sms") - }} - color="primary" - value="sms" /> - } - label={
SMS
} + { + setTriggerOptionsWrapper("sms") + }} + color="primary" + value="sms" + /> + } + label={
SMS
} />
diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 6ed887f3..ef746a6d 100644 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -160,10 +160,6 @@ const LoginDialog = props => { //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); var formtitle = register ?
Login
:
Register
- - // {formtitle} - - console.log("THEME: ", theme.palette.surfaceColor) const basedata =
{ - const { globalUrl, isLoggedIn, isLoaded, } = props; + const { globalUrl, isLoggedIn, isLoaded, removeCookie, cookies} = props; document.title = "Shuffle - Workflows" const alert = useAlert() @@ -83,6 +82,31 @@ 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 ? { const getAvailableWorkflows = () => { fetch(globalUrl+"/api/v1/workflows", { - method: 'GET', + method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, - credentials: "include", - }) + credentials: "include", + }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!") @@ -149,7 +173,7 @@ const Workflows = (props) => { if (isLoggedIn) { alert.error("An error occurred while loading workflows") } else { - window.location = "/login" + handleClickLogout() } return @@ -586,6 +610,10 @@ const Workflows = (props) => { return null } + if (data.workflow.actions === null || data.workflow.actions === undefined) { + return null + } + var actions = data.workflow.actions.length if (data.results !== null) { var results = data.results.length