mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
OAuth: return github teams as a part of user info (enable team sync) (#17797)
* OAuth: github team sync POC * OAuth: minor refactor of github module * OAuth: able to use team shorthands for github team sync * support passing a list of groups via auth-proxy header
This commit is contained in:
parent
4e27ba9646
commit
c2affdee1e
@ -34,7 +34,7 @@ ldap_sync_ttl = 60
|
||||
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
|
||||
whitelist =
|
||||
# Optionally define more headers to sync other user attributes
|
||||
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL`
|
||||
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL Groups:X-WEBAUTH-GROUPS`
|
||||
headers =
|
||||
```
|
||||
|
||||
|
@ -171,6 +171,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *m.ReqContext) {
|
||||
Login: userInfo.Login,
|
||||
Email: userInfo.Email,
|
||||
OrgRoles: map[int64]m.RoleType{},
|
||||
Groups: userInfo.Groups,
|
||||
}
|
||||
|
||||
if userInfo.Role != "" {
|
||||
|
@ -2,6 +2,7 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@ -20,6 +21,15 @@ type SocialGithub struct {
|
||||
teamIds []int
|
||||
}
|
||||
|
||||
type GithubTeam struct {
|
||||
Id int `json:"id"`
|
||||
Slug string `json:"slug"`
|
||||
URL string `json:"html_url"`
|
||||
Organization struct {
|
||||
Login string `json:"login"`
|
||||
} `json:"organization"`
|
||||
}
|
||||
|
||||
var (
|
||||
ErrMissingTeamMembership = &Error{"User not a member of one of the required teams"}
|
||||
ErrMissingOrganizationMembership = &Error{"User not a member of one of the required organizations"}
|
||||
@ -48,8 +58,8 @@ func (s *SocialGithub) IsTeamMember(client *http.Client) bool {
|
||||
}
|
||||
|
||||
for _, teamId := range s.teamIds {
|
||||
for _, membershipId := range teamMemberships {
|
||||
if teamId == membershipId {
|
||||
for _, membership := range teamMemberships {
|
||||
if teamId == membership.Id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -108,14 +118,10 @@ func (s *SocialGithub) FetchPrivateEmail(client *http.Client) (string, error) {
|
||||
return email, nil
|
||||
}
|
||||
|
||||
func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error) {
|
||||
type Record struct {
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]GithubTeam, error) {
|
||||
url := fmt.Sprintf(s.apiUrl + "/teams?per_page=100")
|
||||
hasMore := true
|
||||
ids := make([]int, 0)
|
||||
teams := make([]GithubTeam, 0)
|
||||
|
||||
for hasMore {
|
||||
|
||||
@ -124,27 +130,19 @@ func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error)
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
var records []Record
|
||||
var records []GithubTeam
|
||||
|
||||
err = json.Unmarshal(response.Body, &records)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
newRecords := len(records)
|
||||
existingRecords := len(ids)
|
||||
tempIds := make([]int, (newRecords + existingRecords))
|
||||
copy(tempIds, ids)
|
||||
ids = tempIds
|
||||
|
||||
for i, record := range records {
|
||||
ids[i] = record.Id
|
||||
}
|
||||
teams = append(teams, records...)
|
||||
|
||||
url, hasMore = s.HasMoreRecords(response.Headers)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
func (s *SocialGithub) HasMoreRecords(headers http.Header) (string, bool) {
|
||||
@ -210,11 +208,19 @@ func (s *SocialGithub) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
teamMemberships, err := s.FetchTeamMemberships(client)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user teams: %s", err)
|
||||
}
|
||||
|
||||
teams := convertToGroupList(teamMemberships)
|
||||
|
||||
userInfo := &BasicUserInfo{
|
||||
Name: data.Login,
|
||||
Login: data.Login,
|
||||
Id: fmt.Sprintf("%d", data.Id),
|
||||
Email: data.Email,
|
||||
Name: data.Login,
|
||||
Login: data.Login,
|
||||
Id: fmt.Sprintf("%d", data.Id),
|
||||
Email: data.Email,
|
||||
Groups: teams,
|
||||
}
|
||||
|
||||
organizationsUrl := fmt.Sprintf(s.apiUrl + "/orgs")
|
||||
@ -236,3 +242,26 @@ func (s *SocialGithub) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func (t *GithubTeam) GetShorthand() (string, error) {
|
||||
if t.Organization.Login == "" || t.Slug == "" {
|
||||
return "", errors.New("Error getting team shorthand")
|
||||
}
|
||||
return fmt.Sprintf("@%s/%s", t.Organization.Login, t.Slug), nil
|
||||
}
|
||||
|
||||
func convertToGroupList(t []GithubTeam) []string {
|
||||
groups := make([]string, 0)
|
||||
for _, team := range t {
|
||||
// Group shouldn't be empty string, otherwise team sync will not work properly
|
||||
if team.URL != "" {
|
||||
groups = append(groups, team.URL)
|
||||
}
|
||||
teamShorthand, _ := team.GetShorthand()
|
||||
if teamShorthand != "" {
|
||||
groups = append(groups, teamShorthand)
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ type BasicUserInfo struct {
|
||||
Login string
|
||||
Company string
|
||||
Role string
|
||||
Groups []string
|
||||
}
|
||||
|
||||
type SocialConnector interface {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/multildap"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -246,13 +247,17 @@ func (auth *AuthProxy) LoginViaHeader() (int64, error) {
|
||||
return 0, newError("Auth proxy header property invalid", nil)
|
||||
}
|
||||
|
||||
for _, field := range []string{"Name", "Email", "Login"} {
|
||||
for _, field := range []string{"Name", "Email", "Login", "Groups"} {
|
||||
if auth.headers[field] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if val := auth.ctx.Req.Header.Get(auth.headers[field]); val != "" {
|
||||
reflect.ValueOf(extUser).Elem().FieldByName(field).SetString(val)
|
||||
if field == "Groups" {
|
||||
extUser.Groups = util.SplitString(val)
|
||||
} else {
|
||||
reflect.ValueOf(extUser).Elem().FieldByName(field).SetString(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user