add functionality to assign user to teams through oidc custom claim

This commit is contained in:
viehlieb 2023-01-27 13:49:19 +01:00
parent d3fa4a04c0
commit 3ef25e7be5
1 changed files with 92 additions and 31 deletions

View File

@ -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
}