mirror of https://github.com/grafana/grafana
GitLab could already be used as an authentication backend by properly configuring `auth.generic_oauth`, but then there was no way to authorize users based on their GitLab group membership. This commit adds a `auth.gitlab` backend, similar to `auth.github`, with an `allowed_groups` option that can be set to a list of groups whose members should be allowed access to Grafana.pull/11314/head
parent
aefcb06ff8
commit
7ec146df99
@ -0,0 +1,131 @@ |
||||
package social |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"net/http" |
||||
"regexp" |
||||
|
||||
"github.com/grafana/grafana/pkg/models" |
||||
|
||||
"golang.org/x/oauth2" |
||||
) |
||||
|
||||
type SocialGitlab struct { |
||||
*SocialBase |
||||
allowedDomains []string |
||||
allowedGroups []string |
||||
apiUrl string |
||||
allowSignup bool |
||||
} |
||||
|
||||
var ( |
||||
ErrMissingGroupMembership = &Error{"User not a member of one of the required groups"} |
||||
) |
||||
|
||||
func (s *SocialGitlab) Type() int { |
||||
return int(models.GITLAB) |
||||
} |
||||
|
||||
func (s *SocialGitlab) IsEmailAllowed(email string) bool { |
||||
return isEmailAllowed(email, s.allowedDomains) |
||||
} |
||||
|
||||
func (s *SocialGitlab) IsSignupAllowed() bool { |
||||
return s.allowSignup |
||||
} |
||||
|
||||
func (s *SocialGitlab) IsGroupMember(client *http.Client) 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 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
func (s *SocialGitlab) GetGroups(client *http.Client, url string) ([]string, string) { |
||||
type Group struct { |
||||
FullPath string `json:"full_path"` |
||||
} |
||||
|
||||
var ( |
||||
groups []Group |
||||
next string |
||||
) |
||||
|
||||
if url == "" { |
||||
return nil, next |
||||
} |
||||
|
||||
response, err := HttpGet(client, url) |
||||
if err != nil { |
||||
s.log.Error("Error getting groups from GitLab API", "err", err) |
||||
return nil, next |
||||
} |
||||
|
||||
if err := json.Unmarshal(response.Body, &groups); err != nil { |
||||
s.log.Error("Error parsing JSON from GitLab API", "err", err) |
||||
return nil, next |
||||
} |
||||
|
||||
fullPaths := make([]string, len(groups)) |
||||
for i, group := range groups { |
||||
fullPaths[i] = group.FullPath |
||||
} |
||||
|
||||
if link, ok := response.Headers["Link"]; ok { |
||||
pattern := regexp.MustCompile(`<([^>]+)>; rel="next"`) |
||||
if matches := pattern.FindStringSubmatch(link[0]); matches != nil { |
||||
next = matches[1] |
||||
} |
||||
} |
||||
|
||||
return fullPaths, next |
||||
} |
||||
|
||||
func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) { |
||||
|
||||
var data struct { |
||||
Id int |
||||
Username string |
||||
Email string |
||||
Name string |
||||
State string |
||||
} |
||||
|
||||
response, err := HttpGet(client, s.apiUrl+"/user") |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Error getting user info: %s", err) |
||||
} |
||||
|
||||
err = json.Unmarshal(response.Body, &data) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Error getting user info: %s", err) |
||||
} |
||||
|
||||
if data.State != "active" { |
||||
return nil, fmt.Errorf("User %s is inactive", data.Username) |
||||
} |
||||
|
||||
userInfo := &BasicUserInfo{ |
||||
Name: data.Name, |
||||
Login: data.Username, |
||||
Email: data.Email, |
||||
} |
||||
|
||||
if !s.IsGroupMember(client) { |
||||
return nil, ErrMissingGroupMembership |
||||
} |
||||
|
||||
return userInfo, nil |
||||
} |
Loading…
Reference in new issue