From b47f0ff07f1cdf6bd8d8263829df985b30a5edd7 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 7 Aug 2019 17:05:35 +0300 Subject: [PATCH] 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 --- docs/sources/auth/gitlab.md | 11 +++++++++ pkg/login/social/gitlab_oauth.go | 39 +++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/docs/sources/auth/gitlab.md b/docs/sources/auth/gitlab.md index b6028b0a2a7..aab05ee86dd 100644 --- a/docs/sources/auth/gitlab.md +++ b/docs/sources/auth/gitlab.md @@ -116,3 +116,14 @@ api_url = https://gitlab.com/api/v4 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" >}}) + diff --git a/pkg/login/social/gitlab_oauth.go b/pkg/login/social/gitlab_oauth.go index 21463dabf8f..5c1ee4801c9 100644 --- a/pkg/login/social/gitlab_oauth.go +++ b/pkg/login/social/gitlab_oauth.go @@ -35,17 +35,15 @@ func (s *SocialGitlab) IsSignupAllowed() bool { return s.allowSignup } -func (s *SocialGitlab) IsGroupMember(client *http.Client) bool { +func (s *SocialGitlab) IsGroupMember(groups []string) bool { if len(s.allowedGroups) == 0 { 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 _, group := range groups { - if group == allowedGroup { - return true - } + for _, allowedGroup := range s.allowedGroups { + for _, group := range groups { + if group == allowedGroup { + return true } } } @@ -53,7 +51,18 @@ func (s *SocialGitlab) IsGroupMember(client *http.Client) bool { 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 { FullPath string `json:"full_path"` } @@ -83,6 +92,7 @@ func (s *SocialGitlab) GetGroups(client *http.Client, url string) ([]string, str 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 { pattern := regexp.MustCompile(`<([^>]+)>; rel="next"`) if matches := pattern.FindStringSubmatch(link[0]); matches != nil { @@ -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) } + groups := s.GetGroups(client) + userInfo := &BasicUserInfo{ - Id: fmt.Sprintf("%d", data.Id), - Name: data.Name, - Login: data.Username, - Email: data.Email, + Id: fmt.Sprintf("%d", data.Id), + Name: data.Name, + Login: data.Username, + Email: data.Email, + Groups: groups, } - if !s.IsGroupMember(client) { + if !s.IsGroupMember(groups) { return nil, ErrMissingGroupMembership }