diff --git a/shared.go b/shared.go index a310428..1f119b7 100755 --- a/shared.go +++ b/shared.go @@ -1449,6 +1449,9 @@ func HandleLogout(resp http.ResponseWriter, request *http.Request) { DeleteCache(ctx, fmt.Sprintf("session_%s", userInfo.Session)) DeleteCache(ctx, userInfo.Session) + //store user's last session so we can force sso when user's session change. + userInfo.UsersLastSession = userInfo.Session + userInfo.Session = "" userInfo.ValidatedSessionOrgs = []string{} err = SetUser(ctx, &userInfo, false) @@ -5114,6 +5117,7 @@ func HandleUpdateUser(resp http.ResponseWriter, request *http.Request) { if userInfo.ActiveOrg.Id == foundUser.ActiveOrg.Id { foundUser.Role = t.Role foundUser.Roles = []string{t.Role} + foundUser.ActiveOrg.Role = t.Role } // Getting the specific org and just updating the user in that one @@ -5126,6 +5130,7 @@ func HandleUpdateUser(resp http.ResponseWriter, request *http.Request) { if user.Id == foundUser.Id { user.Role = t.Role user.Roles = []string{t.Role} + user.ActiveOrg.Role = t.Role } users = append(users, user) @@ -10768,6 +10773,7 @@ func HandleChangeUserOrg(resp http.ResponseWriter, request *http.Request) { type ReturnData struct { OrgId string `json:"org_id" datastore:"org_id"` RegionUrl string `json:"region_url" datastore:"region_url"` + SSOTest bool `json:"sso_test"` } var tmpData ReturnData @@ -10800,7 +10806,7 @@ func HandleChangeUserOrg(resp http.ResponseWriter, request *http.Request) { } } - if user.ActiveOrg.Id == fileId { + if user.ActiveOrg.Id == fileId && tmpData.SSOTest == false { log.Printf("[WARNING] User swap to the org \"%s\" - already in the org", tmpData.OrgId) resp.WriteHeader(400) resp.Write([]byte(`{"success": false, "reason": "You are already in that organisation"}`)) @@ -10830,8 +10836,7 @@ func HandleChangeUserOrg(resp http.ResponseWriter, request *http.Request) { return } - //if org.SSOConfig.SSORequired == true && !ArrayContains(user.ValidatedSessionOrgs, tmpData.OrgId) && user.SupportAccess == false { - if org.SSOConfig.SSORequired == true && !ArrayContains(user.ValidatedSessionOrgs, tmpData.OrgId) && user.SupportAccess == false { + if (org.SSOConfig.SSORequired == true && user.UsersLastSession != user.Session && user.SupportAccess == false) || tmpData.SSOTest { baseSSOUrl := org.SSOConfig.SSOEntrypoint redirectKey := "SSO_REDIRECT" @@ -12272,7 +12277,6 @@ func HandleNewHook(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Required fields id and name can't be empty"}`)) return - } validTypes := []string{ @@ -12294,6 +12298,35 @@ func HandleNewHook(resp http.ResponseWriter, request *http.Request) { return } + originalWorkflow, err := GetWorkflow(ctx, requestdata.Workflow) + if err != nil { + log.Printf("[WARNING] Failed getting workflow %s: %s", requestdata.Workflow, err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Workflow doesn't exist"}`)) + return + } + + originalHook, err := GetHook(ctx, newId) + if err == nil { + log.Printf("[WARNING] Hook with ID %s doesn't exist", newId) + } + + if originalWorkflow.OrgId != user.ActiveOrg.Id { + log.Printf("[WARNING] User %s doesn't have access to workflow %s", user.Username, requestdata.Workflow) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if (!(user.SupportAccess || user.Id == originalHook.Owner) || len(user.Id) == 0) && originalHook.Id != "" { + if originalHook.OrgId != user.ActiveOrg.Id && originalHook.OrgId != "" { + log.Printf("[WARNING] User %s doesn't have access to hook %s", user.Username, originalHook.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "User doesn't have access to hook"}`)) + return + } + } + // Let remote endpoint handle access checks (shuffler.io) baseUrl := "https://shuffler.io" if len(os.Getenv("SHUFFLE_GCEPROJECT")) > 0 && len(os.Getenv("SHUFFLE_GCEPROJECT_LOCATION")) > 0 { @@ -12375,6 +12408,33 @@ func HandleNewHook(resp http.ResponseWriter, request *http.Request) { return } + // set the same for the workflow + workflow, err := GetWorkflow(ctx, requestdata.Workflow) + if err != nil { + log.Printf("[WARNING] Failed getting workflow %s: %s", requestdata.Workflow, err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // get the webhook trigger with the same id + for triggerIndex, trigger := range workflow.Triggers { + if trigger.ID == newId { + workflow.Triggers[triggerIndex].Status = "running" + log.Printf("[INFO] Changed status of trigger %s to running", newId) + break + } + } + + // update the workflow + err = SetWorkflow(ctx, *workflow, workflow.ID) + if err != nil { + log.Printf("[WARNING] Failed setting workflow %s: %s", workflow.ID, err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + log.Printf("[INFO] Set up a new hook with ID %s and environment %s", newId, hook.Environment) resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) @@ -12468,6 +12528,24 @@ func HandleDeleteHook(resp http.ResponseWriter, request *http.Request) { return } + // find workflow and set status to stopped + workflow, err := GetWorkflow(ctx, hook.Workflows[0]) + if err != nil { + log.Printf("[WARNING] Failed getting workflow %s: %s", hook.Workflows[0], err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if len(workflow.Triggers) > 0 { + for triggerIndex, trigger := range workflow.Triggers { + if trigger.ID == fileId { + workflow.Triggers[triggerIndex].Status = "stopped" + break + } + } + } + if hook.Environment == "cloud" && project.Environment != "cloud" { log.Printf("[INFO] Should STOP cloud webhook https://shuffler.io/api/v1/hooks/webhook_%s", hook.Id) org, err := GetOrg(ctx, user.ActiveOrg.Id) @@ -18757,41 +18835,46 @@ func HandleOpenId(resp http.ResponseWriter, request *http.Request) { } expiration := time.Now().Add(3600 * time.Second) - //if len(user.Session) == 0 { - log.Printf("[INFO] User does NOT have session - creating") - sessionToken := uuid.NewV4().String() + if len(user.Session) == 0 { + log.Printf("[INFO] User does NOT have session - creating") + sessionToken := uuid.NewV4().String() - newCookie := http.Cookie{ - Name: "session_token", - Value: sessionToken, - Expires: expiration, - Path: "/", - } + newCookie := http.Cookie{ + Name: "session_token", + Value: sessionToken, + Expires: expiration, + Path: "/", + } - if project.Environment == "cloud" { - newCookie.Domain = ".shuffler.io" - newCookie.Secure = true - newCookie.HttpOnly = true - } + if project.Environment == "cloud" { + newCookie.Domain = ".shuffler.io" + newCookie.Secure = true + newCookie.HttpOnly = true + } - http.SetCookie(resp, &newCookie) + http.SetCookie(resp, &newCookie) - newCookie.Name = "__session" - http.SetCookie(resp, &newCookie) + newCookie.Name = "__session" + http.SetCookie(resp, &newCookie) - err = SetSession(ctx, user, sessionToken) - if err != nil { - log.Printf("[WARNING] Error creating session for user: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) - return - } + err = SetSession(ctx, user, sessionToken) + if err != nil { + log.Printf("[WARNING] Error creating session for user: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) + return + } - user.Session = sessionToken + user.Session = sessionToken + } user.LoginInfo = append(user.LoginInfo, LoginInfo{ IP: GetRequestIp(request), Timestamp: time.Now().Unix(), }) + + //Store users last session as new session so user don't have to go through sso again while changing org. + user.UsersLastSession = user.Session + err = SetUser(ctx, &user, false) if err != nil { log.Printf("[WARNING] Failed updating user when setting session: %s", err) @@ -18822,40 +18905,45 @@ func HandleOpenId(resp http.ResponseWriter, request *http.Request) { } expiration := time.Now().Add(3600 * time.Second) - //if len(user.Session) == 0 { - log.Printf("[INFO] User does NOT have session - creating") - sessionToken := uuid.NewV4().String() - newCookie := &http.Cookie{ - Name: "session_token", - Value: sessionToken, - Expires: expiration, - Path: "/", - } + if len(user.Session) == 0 { + log.Printf("[INFO] User does NOT have session - creating") + sessionToken := uuid.NewV4().String() + newCookie := &http.Cookie{ + Name: "session_token", + Value: sessionToken, + Expires: expiration, + Path: "/", + } - if project.Environment == "cloud" { - newCookie.Domain = ".shuffler.io" - newCookie.Secure = true - newCookie.HttpOnly = true - } + if project.Environment == "cloud" { + newCookie.Domain = ".shuffler.io" + newCookie.Secure = true + newCookie.HttpOnly = true + } - http.SetCookie(resp, newCookie) + http.SetCookie(resp, newCookie) - newCookie.Name = "__session" - http.SetCookie(resp, newCookie) + newCookie.Name = "__session" + http.SetCookie(resp, newCookie) - err = SetSession(ctx, user, sessionToken) - if err != nil { - log.Printf("[WARNING] Error creating session for user: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) - return - } + err = SetSession(ctx, user, sessionToken) + if err != nil { + log.Printf("[WARNING] Error creating session for user: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) + return + } - user.Session = sessionToken + user.Session = sessionToken + } user.LoginInfo = append(user.LoginInfo, LoginInfo{ IP: GetRequestIp(request), Timestamp: time.Now().Unix(), }) + + //Store users last session as new session so user don't have to go through sso again while changing org. + user.UsersLastSession = user.Session + err = SetUser(ctx, &user, false) if err != nil { log.Printf("[WARNING] Failed updating user when setting session: %s", err) @@ -18952,6 +19040,10 @@ func HandleOpenId(resp http.ResponseWriter, request *http.Request) { } newUser.Session = sessionToken + + //Store users last session as new session so user don't have to go through sso again while changing org. + newUser.UsersLastSession = sessionToken + err = SetUser(ctx, newUser, true) if err != nil { log.Printf("[WARNING] Failed setting new user in DB: %s", err) @@ -18990,7 +19082,8 @@ func HandleSSO(resp http.ResponseWriter, request *http.Request) { } if len(backendUrl) > 0 { - redirectUrl = fmt.Sprintf("%s/workflows", backendUrl) + // redirectUrl = fmt.Sprintf("%s/workflows", backendUrl) + redirectUrl = backendUrl } if project.Environment == "cloud" { @@ -19196,46 +19289,51 @@ func HandleSSO(resp http.ResponseWriter, request *http.Request) { //log.Printf("SESSION: %s", user.Session) expiration := time.Now().Add(3600 * time.Second) - // if len(user.Session) == 0 { - log.Printf("[INFO] User does NOT have session - creating") - sessionToken := uuid.NewV4().String() - newCookie := &http.Cookie{ - Name: "session_token", - Value: sessionToken, - Expires: expiration, - Path: "/", - } + if len(user.Session) == 0 { + log.Printf("[INFO] User does NOT have session - creating") + sessionToken := uuid.NewV4().String() + newCookie := &http.Cookie{ + Name: "session_token", + Value: sessionToken, + Expires: expiration, + Path: "/", + } - if project.Environment == "cloud" { - newCookie.Domain = ".shuffler.io" - newCookie.Secure = true - newCookie.HttpOnly = true - } + if project.Environment == "cloud" { + newCookie.Domain = ".shuffler.io" + newCookie.Secure = true + newCookie.HttpOnly = true + } - http.SetCookie(resp, newCookie) + http.SetCookie(resp, newCookie) - newCookie.Name = "__session" - http.SetCookie(resp, newCookie) + newCookie.Name = "__session" + http.SetCookie(resp, newCookie) - err = SetSession(ctx, user, sessionToken) - if err != nil { - log.Printf("[WARNING] Error creating session for user: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) - return - } + err = SetSession(ctx, user, sessionToken) + if err != nil { + log.Printf("[WARNING] Error creating session for user: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) + return + } - user.LoginInfo = append(user.LoginInfo, LoginInfo{ - IP: GetRequestIp(request), - Timestamp: time.Now().Unix(), - }) + user.LoginInfo = append(user.LoginInfo, LoginInfo{ + IP: GetRequestIp(request), + Timestamp: time.Now().Unix(), + }) - user.Session = sessionToken + user.Session = sessionToken + } // user.LoginInfo = append(user.LoginInfo, LoginInfo{ // IP: GetRequestIp(request), // Timestamp: time.Now().Unix(), // }) // } + + //store user's last session so don't have to go through sso again while changing org. + user.UsersLastSession = user.Session + err = SetUser(ctx, &user, false) if err != nil { log.Printf("[WARNING] Failed updating user when setting session: %s", err) @@ -19270,41 +19368,43 @@ func HandleSSO(resp http.ResponseWriter, request *http.Request) { } expiration := time.Now().Add(3600 * time.Second) - // if len(user.Session) == 0 { - log.Printf("[INFO] User does NOT have session - creating") - sessionToken := uuid.NewV4().String() - newCookie := &http.Cookie{ - Name: "session_token", - Value: sessionToken, - Expires: expiration, - Path: "/", - } + if len(user.Session) == 0 { + log.Printf("[INFO] User does NOT have session - creating") + sessionToken := uuid.NewV4().String() + newCookie := &http.Cookie{ + Name: "session_token", + Value: sessionToken, + Expires: expiration, + Path: "/", + } - if project.Environment == "cloud" { - newCookie.Domain = ".shuffler.io" - newCookie.Secure = true - newCookie.HttpOnly = true - } + if project.Environment == "cloud" { + newCookie.Domain = ".shuffler.io" + newCookie.Secure = true + newCookie.HttpOnly = true + } - http.SetCookie(resp, newCookie) + http.SetCookie(resp, newCookie) - newCookie.Name = "__session" - http.SetCookie(resp, newCookie) + newCookie.Name = "__session" + http.SetCookie(resp, newCookie) - err = SetSession(ctx, user, sessionToken) - if err != nil { - log.Printf("[WARNING] Error creating session for user: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) - return - } + err = SetSession(ctx, user, sessionToken) + if err != nil { + log.Printf("[WARNING] Error creating session for user: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting session"}`))) + return + } - user.Session = sessionToken - user.LoginInfo = append(user.LoginInfo, LoginInfo{ - IP: GetRequestIp(request), - Timestamp: time.Now().Unix(), - }) - // } + user.Session = sessionToken + user.LoginInfo = append(user.LoginInfo, LoginInfo{ + IP: GetRequestIp(request), + Timestamp: time.Now().Unix(), + }) + } + //Store user's last session so don't have to go through sso again while changing org. + user.UsersLastSession = user.Session err = SetUser(ctx, &user, false) if err != nil { log.Printf("[WARNING] Failed updating user when setting session: %s", err) @@ -19409,6 +19509,9 @@ func HandleSSO(resp http.ResponseWriter, request *http.Request) { Timestamp: time.Now().Unix(), }) + //Store user's last session so don't have to go through sso again while changing org. + newUser.UsersLastSession = sessionToken + err = SetUser(ctx, newUser, true) if err != nil { log.Printf("[WARNING] Failed setting new user in DB: %s", err) diff --git a/structs.go b/structs.go index c3f2217..a942e7b 100755 --- a/structs.go +++ b/structs.go @@ -591,6 +591,7 @@ type User struct { GeneratedUsername string `datastore:"generated_username" json:"generated_username"` SessionLogin bool `datastore:"session_login" json:"session_login"` // Whether it's a login with session or API (used to verify access) ValidatedSessionOrgs []string `datastore:"validated_session_orgs" json:"validated_session_orgs"` // Orgs that have been used in the current session for the user + UsersLastSession string `datastore:"users_last_session" json:"users_last_session"` // Starting web3 integration EthInfo EthInfo `datastore:"eth_info" json:"eth_info"`