code.gitea.io/gitea@v1.21.7/services/auth/source/source_group_sync.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package source 5 6 import ( 7 "context" 8 "fmt" 9 10 "code.gitea.io/gitea/models" 11 "code.gitea.io/gitea/models/organization" 12 user_model "code.gitea.io/gitea/models/user" 13 "code.gitea.io/gitea/modules/container" 14 "code.gitea.io/gitea/modules/log" 15 ) 16 17 type syncType int 18 19 const ( 20 syncAdd syncType = iota 21 syncRemove 22 ) 23 24 // SyncGroupsToTeams maps authentication source groups to organization and team memberships 25 func SyncGroupsToTeams(ctx context.Context, user *user_model.User, sourceUserGroups container.Set[string], sourceGroupTeamMapping map[string]map[string][]string, performRemoval bool) error { 26 orgCache := make(map[string]*organization.Organization) 27 teamCache := make(map[string]*organization.Team) 28 return SyncGroupsToTeamsCached(ctx, user, sourceUserGroups, sourceGroupTeamMapping, performRemoval, orgCache, teamCache) 29 } 30 31 // SyncGroupsToTeamsCached maps authentication source groups to organization and team memberships 32 func SyncGroupsToTeamsCached(ctx context.Context, user *user_model.User, sourceUserGroups container.Set[string], sourceGroupTeamMapping map[string]map[string][]string, performRemoval bool, orgCache map[string]*organization.Organization, teamCache map[string]*organization.Team) error { 33 membershipsToAdd, membershipsToRemove := resolveMappedMemberships(sourceUserGroups, sourceGroupTeamMapping) 34 35 if performRemoval { 36 if err := syncGroupsToTeamsCached(ctx, user, membershipsToRemove, syncRemove, orgCache, teamCache); err != nil { 37 return fmt.Errorf("could not sync[remove] user groups: %w", err) 38 } 39 } 40 41 if err := syncGroupsToTeamsCached(ctx, user, membershipsToAdd, syncAdd, orgCache, teamCache); err != nil { 42 return fmt.Errorf("could not sync[add] user groups: %w", err) 43 } 44 45 return nil 46 } 47 48 func resolveMappedMemberships(sourceUserGroups container.Set[string], sourceGroupTeamMapping map[string]map[string][]string) (map[string][]string, map[string][]string) { 49 membershipsToAdd := map[string][]string{} 50 membershipsToRemove := map[string][]string{} 51 for group, memberships := range sourceGroupTeamMapping { 52 isUserInGroup := sourceUserGroups.Contains(group) 53 if isUserInGroup { 54 for org, teams := range memberships { 55 membershipsToAdd[org] = append(membershipsToAdd[org], teams...) 56 } 57 } else { 58 for org, teams := range memberships { 59 membershipsToRemove[org] = append(membershipsToRemove[org], teams...) 60 } 61 } 62 } 63 return membershipsToAdd, membershipsToRemove 64 } 65 66 func syncGroupsToTeamsCached(ctx context.Context, user *user_model.User, orgTeamMap map[string][]string, action syncType, orgCache map[string]*organization.Organization, teamCache map[string]*organization.Team) error { 67 for orgName, teamNames := range orgTeamMap { 68 var err error 69 org, ok := orgCache[orgName] 70 if !ok { 71 org, err = organization.GetOrgByName(ctx, orgName) 72 if err != nil { 73 if organization.IsErrOrgNotExist(err) { 74 // organization must be created before group sync 75 log.Warn("group sync: Could not find organisation %s: %v", orgName, err) 76 continue 77 } 78 return err 79 } 80 orgCache[orgName] = org 81 } 82 for _, teamName := range teamNames { 83 team, ok := teamCache[orgName+teamName] 84 if !ok { 85 team, err = org.GetTeam(ctx, teamName) 86 if err != nil { 87 if organization.IsErrTeamNotExist(err) { 88 // team must be created before group sync 89 log.Warn("group sync: Could not find team %s: %v", teamName, err) 90 continue 91 } 92 return err 93 } 94 teamCache[orgName+teamName] = team 95 } 96 97 isMember, err := organization.IsTeamMember(ctx, org.ID, team.ID, user.ID) 98 if err != nil { 99 return err 100 } 101 102 if action == syncAdd && !isMember { 103 if err := models.AddTeamMember(ctx, team, user.ID); err != nil { 104 log.Error("group sync: Could not add user to team: %v", err) 105 return err 106 } 107 } else if action == syncRemove && isMember { 108 if err := models.RemoveTeamMember(ctx, team, user.ID); err != nil { 109 log.Error("group sync: Could not remove user from team: %v", err) 110 return err 111 } 112 } 113 } 114 } 115 return nil 116 }