From 3ef25e7be55cc357cfc9538d38a1b563a1838166 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Fri, 27 Jan 2023 13:49:19 +0100 Subject: [PATCH] add functionality to assign user to teams through oidc custom claim --- pkg/modules/auth/openid/openid.go | 123 ++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/pkg/modules/auth/openid/openid.go b/pkg/modules/auth/openid/openid.go index 3ede85ba6..0335166d4 100644 --- a/pkg/modules/auth/openid/openid.go +++ b/pkg/modules/auth/openid/openid.go @@ -150,6 +150,7 @@ func HandleCallback(c echo.Context) error { // Extract custom claims cl := &claims{} + err = idToken.Claims(cl) if err != nil { log.Errorf("Error getting token claims for provider %s: %v", provider.Name, err) @@ -204,57 +205,117 @@ func HandleCallback(c echo.Context) error { return handler.HandleHTTPError(err, c) } - // Check if we have seen these teams before - if len(cl.Teams) > 0 { - teams, err := GetOrCreateTeamsByNames(s, cl.Teams, u) + // does the oidc token contain well formed "vikunja_groups" through vikunja_scope + teamData, err := getTeamDataFromToken(cl.VikunjaGroups, provider) + if err != nil { + 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. + if len(teamData) > 0 { + log.Debugf("TeamData is set %v", teamData) + teams, err := GetOrCreateTeamsByNames(s, teamData, u) if err != nil { log.Errorf("Error verifying team for name %v, got %v", cl.Name, teams, err) return err - } else { - for _, team := range teams { - tm := models.TeamMember{TeamID: team.ID, Username: u.Username} - err := tm.CheckMembership(s) - if err == nil { - tm.Create(s, u) + } + + for _, team := range teams { + tm := models.TeamMember{TeamID: team.ID, Username: u.Username} + exists, err := tm.CheckMembership(s) + if !exists { + err = tm.Create(s, u) + if err != nil { + log.Debugf("Could not assign %v to %v. %v", u.Username, team.Name, err) } + } else { + log.Debugf("Team exists? %v or error: %v", exists, err) } } } + err = s.Commit() if err != nil { + _ = s.Rollback() + log.Errorf("Error creating new Team for provider %s: %v", provider.Name, err) return handler.HandleHTTPError(err, c) } // Create token return auth.NewUserAuthTokenResponse(u, c, false) } -func GetOrCreateTeamsByNames(s *xorm.Session, teamNames []string, u *user.User) (te []models.Team, err error) { - te = []models.Team{} - for _, t := range teamNames { - team, err := models.GetTeamsByName(s, t) - - // if team does not exists, create it - if models.IsErrTeamsDoNotExist(err) { - log.Debugf("No such Team: %v, create %v ..", t, team) - tea := &models.Team{ - Name: t, +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 + if team["name"] != nil { + name = team["name"].(string) } - err := tea.Create(s, u) - if err != nil { - log.Errorf("Teams: %v, err: %v", tea, err) - } else { - te = append(te, *tea) + if team["description"] != nil { + description = team["description"].(string) } - } else { - // if multiple teams with same name are found, - if len(team) == 1 { - // append team to return value - te = append(te, *team[len(team)-1]) - } else { - log.Debugf("Multiple Teams have the same name: %v, ignore assignment of %v", team[len(team)-1].Name, u.Name) + if team["oidcID"] != nil { + switch t := team["oidcID"].(type) { + case int64: + oidcID = fmt.Sprintf("%v", team["oidcID"]) + case string: + oidcID = string(team["oidcID"].(string)) + case float64: + fl := float64(team["oidcID"].(float64)) + oidcID = fmt.Sprintf("%v", fl) + default: + log.Errorf("No oidcID assigned for %v or type %v not supported", team, t) + } } + if name == "" || oidcID == "" { + log.Errorf("Claim of your custom scope does not hold name or oidcID for automatic group assignment through oidc provider. Please check %s", provider.Name) + return teamData, &user.ErrOpenIDCustomScopeMalformed{} + } + teamData = append(teamData, TeamData{TeamName: name, OidcID: oidcID, Description: description}) } } + return teamData, nil +} + +func CreateTeamWithData(s *xorm.Session, teamData TeamData, u *user.User) (team *models.Team, err error) { + tea := &models.Team{ + Name: teamData.TeamName, + Description: teamData.Description, + OidcID: teamData.OidcID, + } + log.Debugf("Teams: %v", tea.OidcID) + + err = tea.Create(s, u) + return tea, err +} + +// this functions creates an array of existing teams that was generated from the oidc data. +func GetOrCreateTeamsByNames(s *xorm.Session, teamData []TeamData, u *user.User) (te []models.Team, err error) { + te = []models.Team{} + // Procedure can only be successful if oidcID is set and converted to string + for _, oidcTeam := range teamData { + team, err := models.GetTeamByOidcIDAndName(s, oidcTeam.OidcID, oidcTeam.TeamName) + if err != nil { + log.Debugf("Team with oidc_id %v and name %v does not exist. Create Team.. ", oidcTeam.OidcID, oidcTeam.TeamName) + newTeam, err := CreateTeamWithData(s, oidcTeam, u) + if err != nil { + return te, err + } + te = append(te, *newTeam) + } else { + log.Debugf("Team with oidc_id %v and name %v already exists.", team.OidcID, team.Name) + te = append(te, team) + } + + } + log.Debugf("Array: %v", te) return te, err }