Commit b47f0ff0 by Alexander Zobnin Committed by GitHub

OAuth: return GitLab groups as a part of user info (enable team sync) (#18388)

* GitLab OAuth: GetGroups refactor

* GitLab OAuth: fetch groups into UserInfo

* GitLab OAuth: minor refactor

* GitLab OAuth: team sync docs
parent b424e12a
...@@ -116,3 +116,14 @@ api_url = https://gitlab.com/api/v4 ...@@ -116,3 +116,14 @@ api_url = https://gitlab.com/api/v4
allowed_groups = example, foo/bar allowed_groups = example, foo/bar
``` ```
### Team Sync (Enterprise only)
> Only available in Grafana Enterprise v6.4+
With Team Sync you can map your GitLab groups to teams in Grafana so that your users will automatically be added to
the correct teams.
Your GitLab groups can be referenced in the same way as `allowed_groups`, like `example` or `foo/bar`.
[Learn more about Team Sync]({{< relref "auth/enhanced_ldap.md" >}})
...@@ -35,12 +35,11 @@ func (s *SocialGitlab) IsSignupAllowed() bool { ...@@ -35,12 +35,11 @@ func (s *SocialGitlab) IsSignupAllowed() bool {
return s.allowSignup return s.allowSignup
} }
func (s *SocialGitlab) IsGroupMember(client *http.Client) bool { func (s *SocialGitlab) IsGroupMember(groups []string) bool {
if len(s.allowedGroups) == 0 { if len(s.allowedGroups) == 0 {
return true return true
} }
for groups, url := s.GetGroups(client, s.apiUrl+"/groups"); groups != nil; groups, url = s.GetGroups(client, url) {
for _, allowedGroup := range s.allowedGroups { for _, allowedGroup := range s.allowedGroups {
for _, group := range groups { for _, group := range groups {
if group == allowedGroup { if group == allowedGroup {
...@@ -48,12 +47,22 @@ func (s *SocialGitlab) IsGroupMember(client *http.Client) bool { ...@@ -48,12 +47,22 @@ func (s *SocialGitlab) IsGroupMember(client *http.Client) bool {
} }
} }
} }
}
return false return false
} }
func (s *SocialGitlab) GetGroups(client *http.Client, url string) ([]string, string) { func (s *SocialGitlab) GetGroups(client *http.Client) []string {
groups := make([]string, 0)
for page, url := s.GetGroupsPage(client, s.apiUrl+"/groups"); page != nil; page, url = s.GetGroupsPage(client, url) {
groups = append(groups, page...)
}
return groups
}
// GetGroupsPage returns groups and link to the next page if response is paginated
func (s *SocialGitlab) GetGroupsPage(client *http.Client, url string) ([]string, string) {
type Group struct { type Group struct {
FullPath string `json:"full_path"` FullPath string `json:"full_path"`
} }
...@@ -83,6 +92,7 @@ func (s *SocialGitlab) GetGroups(client *http.Client, url string) ([]string, str ...@@ -83,6 +92,7 @@ func (s *SocialGitlab) GetGroups(client *http.Client, url string) ([]string, str
fullPaths[i] = group.FullPath fullPaths[i] = group.FullPath
} }
// GitLab uses Link header with "rel" set to prev/next/first/last page. We need "next".
if link, ok := response.Headers["Link"]; ok { if link, ok := response.Headers["Link"]; ok {
pattern := regexp.MustCompile(`<([^>]+)>; rel="next"`) pattern := regexp.MustCompile(`<([^>]+)>; rel="next"`)
if matches := pattern.FindStringSubmatch(link[0]); matches != nil { if matches := pattern.FindStringSubmatch(link[0]); matches != nil {
...@@ -117,14 +127,17 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi ...@@ -117,14 +127,17 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
return nil, fmt.Errorf("User %s is inactive", data.Username) return nil, fmt.Errorf("User %s is inactive", data.Username)
} }
groups := s.GetGroups(client)
userInfo := &BasicUserInfo{ userInfo := &BasicUserInfo{
Id: fmt.Sprintf("%d", data.Id), Id: fmt.Sprintf("%d", data.Id),
Name: data.Name, Name: data.Name,
Login: data.Username, Login: data.Username,
Email: data.Email, Email: data.Email,
Groups: groups,
} }
if !s.IsGroupMember(client) { if !s.IsGroupMember(groups) {
return nil, ErrMissingGroupMembership return nil, ErrMissingGroupMembership
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment