diff --git a/pkg/models/error.go b/pkg/models/error.go index c63ee71eb..c0d21de92 100644 --- a/pkg/models/error.go +++ b/pkg/models/error.go @@ -1072,7 +1072,6 @@ func (err ErrTeamNameCannotBeEmpty) HTTPError() web.HTTPError { return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"} } -// ErrTeamDoesNotExist represents an error where a team does not exist type ErrTeamDoesNotExist struct { TeamID int64 } @@ -1095,28 +1094,6 @@ func (err ErrTeamDoesNotExist) HTTPError() web.HTTPError { return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."} } -type ErrTeamsDoNotExist struct { - Name string -} - -// IsErrTeamDoNotExist checks if an error is ErrTeamDoesNotExist. -func IsErrTeamsDoNotExist(err error) bool { - _, ok := err.(ErrTeamsDoNotExist) - return ok -} - -func (err ErrTeamsDoNotExist) Error() string { - return fmt.Sprintf("Team does not exist [Team Name: %v]", err.Name) -} - -// ErrCodeTeamDoesNotExist holds the unique world-error code of this error -const ErrCodeTeamsDoNotExist = 6002 - -// HTTPError holds the http error description -func (err ErrTeamsDoNotExist) HTTPError() web.HTTPError { - return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "No team with given name exists."} -} - // ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace type ErrTeamAlreadyHasAccess struct { TeamID int64 @@ -1213,6 +1190,51 @@ func (err ErrTeamDoesNotHaveAccessToList) HTTPError() web.HTTPError { return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."} } +type ErrTeamsDoNotExist struct { + Name string +} + +// IsErrTeamDoNotExist checks if an error is ErrTeamDoesNotExist. +func IsErrTeamsDoNotExist(err error) bool { + _, ok := err.(ErrTeamsDoNotExist) + return ok +} + +func (err ErrTeamsDoNotExist) Error() string { + return fmt.Sprintf("Team does not exist [Team Name: %v]", err.Name) +} + +// ErrCodeTeamDoesNotExist holds the unique world-error code of this error +const ErrCodeTeamsDoNotExist = 6008 + +// HTTPError holds the http error description +func (err ErrTeamsDoNotExist) HTTPError() web.HTTPError { + return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "No team with given name exists."} +} + +// ErrOIDCTeamsDoNotExistForUser represents an error where an oidcTeam does not exist for the user +type ErrOIDCTeamsDoNotExistForUser struct { + UserID int64 +} + +// IsErrOIDCTeamsDoNotExistForUser checks if an error is ErrOIDCTeamsDoNotExistForUser. +func IsErrOIDCTeamsDoNotExistForUser(err error) bool { + _, ok := err.(ErrTeamDoesNotExist) + return ok +} + +func (err ErrOIDCTeamsDoNotExistForUser) Error() string { + return fmt.Sprintf("No Oidc exists for User [User ID: %d]", err.UserID) +} + +// ErrCodeTeamDoesNotExist holds the unique world-error code of this error +const ErrCodeOIDCTeamsDoNotExistForUser = 6009 + +// HTTPError holds the http error description +func (err ErrOIDCTeamsDoNotExistForUser) HTTPError() web.HTTPError { + return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."} +} + // ==================== // User <-> List errors // ==================== diff --git a/pkg/models/teams.go b/pkg/models/teams.go index f0ecbf541..9becb12a6 100644 --- a/pkg/models/teams.go +++ b/pkg/models/teams.go @@ -94,6 +94,12 @@ type TeamUser struct { TeamID int64 `json:"-"` } +type TeamData struct { + TeamName string + OidcID string + Description string +} + // GetTeamByID gets a team by its ID func GetTeamByID(s *xorm.Session, id int64) (team *Team, err error) { if id < 1 { @@ -143,16 +149,30 @@ func GetTeamsByName(s *xorm.Session, name string) (teams []*Team, err error) { // GetTeamByOidcIDAndName gets teams where oidc_id and name match parameters // For oidc team creation oidcID and Name need to be set -func GetTeamByOidcIDAndName(s *xorm.Session, id string, name string) (team Team, err error) { +func GetTeamByOidcIDAndName(s *xorm.Session, oidcID string, teamName string) (team Team, err error) { exists, err := s. Table("teams"). - Where("oidc_id = ? AND name = ?", id, name). + Where("oidc_id = ? AND name = ?", oidcID, teamName). Get(&team) log.Debugf("GetTeamByOidcIDAndName: %v, exists: %v", team.Name, exists) if exists && err == nil { return team, nil } - return team, ErrTeamsDoNotExist{id} + return team, ErrTeamsDoNotExist{oidcID} +} + +func FindAllOidcTeamIDsForUser(s *xorm.Session, userID int64) (ts []int64, err error) { + err = s. + Table("team_members"). + Where("user_id = ? ", userID). + Join("RIGHT", "teams", "teams.id = team_members.team_id"). + Where("teams.oidc_id != ?", ""). + Cols("teams.id"). + Find(&ts) + if ts == nil || err != nil { + return ts, ErrOIDCTeamsDoNotExistForUser{userID} + } + return ts, nil } func addMoreInfoToTeams(s *xorm.Session, teams []*Team) (err error) { diff --git a/pkg/modules/auth/openid/openid.go b/pkg/modules/auth/openid/openid.go index 1780bedc6..12481c910 100644 --- a/pkg/modules/auth/openid/openid.go +++ b/pkg/modules/auth/openid/openid.go @@ -211,16 +211,22 @@ func HandleCallback(c echo.Context) error { log.Errorf("Error creating teams for user and vikunja groups %s: %v", cl.VikunjaGroups, err) return handler.HandleHTTPError(err, c) } - // check if we have seen these teams before. - // find or create Teams and assign user as teammember. + + //TODO: fix this error check + // nil is no problem + if len(teamData) > 0 { + //find old teams for user through oidc + oldOidcTeams, _ := models.FindAllOidcTeamIDsForUser(s, u.ID) + // check if we have seen these teams before. + // find or create Teams and assign user as teammember. + var oidcTeams []int64 log.Debugf("TeamData is set %v", teamData) teams, err := GetOrCreateTeamsByOIDCAndNames(s, teamData, u) if err != nil { log.Errorf("Error verifying team for name %v, got %v", cl.Name, teams, err) return err } - for _, team := range teams { tm := models.TeamMember{TeamID: team.ID, Username: u.Username} exists, err := tm.CheckMembership(s) @@ -232,9 +238,10 @@ func HandleCallback(c echo.Context) error { } else { log.Debugf("Team exists? %v or error: %v", exists, err) } + oidcTeams = append(oidcTeams, team.ID) } + SignOutFromOrDeleteTeamsByID(s, u, notIn(oldOidcTeams, oidcTeams)) } - err = s.Commit() if err != nil { _ = s.Rollback() @@ -245,13 +252,30 @@ func HandleCallback(c echo.Context) error { return auth.NewUserAuthTokenResponse(u, c, false) } +func SignOutFromOrDeleteTeamsByID(s *xorm.Session, u *user.User, teamIDs []int64) { + for _, teamID := range teamIDs { + tm := models.TeamMember{TeamID: teamID, Username: u.Username} + err := tm.Delete(s, u) + if err != nil { + team, err := models.GetTeamByID(s, teamID) + if err != nil { + log.Errorf("Cannot find team with id: %v, err: %v", teamID, err) + } else { + err = team.Delete(s, u) + if err != nil { + log.Errorf("Cannot delete team %v", err) + } + } + } + } +} + func getTeamDataFromToken(groups interface{}, provider *Provider) (teamData []TeamData, err error) { teamData = []TeamData{} if groups != nil { el := groups.([]interface{}) for _, data := range el { team := data.(map[string]interface{}) - log.Debugf("%s", team) var name string var description string var oidcID string @@ -397,3 +421,23 @@ func getOrCreateUser(s *xorm.Session, cl *claims, issuer, subject string) (u *us return } + +// find the elements which appear in slice1,but not in slice2 +func notIn(slice1 []int64, slice2 []int64) []int64 { + var diff []int64 + + for _, s1 := range slice1 { + found := false + for _, s2 := range slice2 { + if s1 == s2 { + found = true + break + } + } + // String not found. We add it to return slice + if !found { + diff = append(diff, s1) + } + } + return diff +}