From 7ea579bb71cf1d505c815d5907b4d8910772a488 Mon Sep 17 00:00:00 2001 From: Garrett Bjerkhoel Date: Tue, 28 Apr 2015 20:19:48 -0700 Subject: [PATCH 1/5] Add team_ids configuration option --- conf/defaults.ini | 1 + pkg/social/social.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 6bb3fb80857..7345f18520e 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -140,6 +140,7 @@ enabled = false client_id = some_id client_secret = some_secret scopes = user:email +team_ids = auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token api_url = https://api.github.com/user diff --git a/pkg/social/social.go b/pkg/social/social.go index 47c7ea5dc38..c19784e34e1 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -75,7 +75,8 @@ func NewOAuthService() { // GitHub. if name == "github" { setting.OAuthService.GitHub = true - SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup} + teamIds := sec.Key("team_ids").Ints(",") + SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup, teamIds: teamIds} } // Google. @@ -105,6 +106,7 @@ type SocialGithub struct { allowedDomains []string ApiUrl string allowSignup bool + teamIds []int } func (s *SocialGithub) Type() int { From 979d0ca70f5aec638ce6e6bb8c8f69f47973f221 Mon Sep 17 00:00:00 2001 From: Garrett Bjerkhoel Date: Tue, 28 Apr 2015 20:21:44 -0700 Subject: [PATCH 2/5] Add new error type for team membership permissions --- pkg/social/social.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/social/social.go b/pkg/social/social.go index c19784e34e1..752ed7114c2 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "errors" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -109,6 +110,10 @@ type SocialGithub struct { teamIds []int } +var ( + ErrMissingTeamMembership = errors.New("User not a member of one of the required teams") +) + func (s *SocialGithub) Type() int { return int(models.GITHUB) } From eb37fc089b01aba3d046635e60596e8d82dc1601 Mon Sep 17 00:00:00 2001 From: Garrett Bjerkhoel Date: Tue, 28 Apr 2015 20:22:21 -0700 Subject: [PATCH 3/5] Check for active team membership when fetching s.UserInfo --- pkg/social/social.go | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/pkg/social/social.go b/pkg/social/social.go index 752ed7114c2..2b716495d53 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" "errors" + "net/http" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -126,6 +127,28 @@ func (s *SocialGithub) IsSignupAllowed() bool { return s.allowSignup } +func (s *SocialGithub) IsTeamMember(client *http.Client, username string, teamId int) bool { + var data struct { + Url string `json:"url"` + State string `json:"state"` + } + + membershipUrl := fmt.Sprintf("https://api.github.com/teams/%d/memberships/%s", teamId, username) + r, err := client.Get(membershipUrl) + if err != nil { + return false + } + + defer r.Body.Close() + + if err = json.NewDecoder(r.Body).Decode(&data); err != nil { + return false + } + + active := data.State == "active" + return active +} + func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { var data struct { Id int `json:"id"` @@ -146,11 +169,23 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { return nil, err } - return &BasicUserInfo{ + userInfo := &BasicUserInfo{ Identity: strconv.Itoa(data.Id), Name: data.Name, Email: data.Email, - }, nil + } + + if len(s.teamIds) > 0 { + for _, teamId := range s.teamIds { + if s.IsTeamMember(client, data.Name, teamId) { + return userInfo, nil + } + } + + return nil, ErrMissingTeamMembership + } else { + return userInfo, nil + } } // ________ .__ From 1d7f9452685f5f2d41224afbf659bb922636a8bc Mon Sep 17 00:00:00 2001 From: Garrett Bjerkhoel Date: Tue, 28 Apr 2015 20:22:45 -0700 Subject: [PATCH 4/5] Handle special error case if connect.UserInfo returns an error --- pkg/api/login_oauth.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 11d62754a18..d89a7237d35 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -45,7 +45,11 @@ func OAuthLogin(ctx *middleware.Context) { userInfo, err := connect.UserInfo(token) if err != nil { - ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err) + if err == social.ErrMissingTeamMembership { + ctx.Redirect(setting.AppSubUrl + "/login?missing_team_membership=1") + } else { + ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err) + } return } From 1fdc5277ae875b50ee680dcc7f162ef716cdc9c4 Mon Sep 17 00:00:00 2001 From: Garrett Bjerkhoel Date: Tue, 28 Apr 2015 20:38:05 -0700 Subject: [PATCH 5/5] Update documentation for team_ids option --- docs/sources/installation/configuration.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index a79a8494d73..a1dfb6ac035 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -179,6 +179,7 @@ Client ID and a Client Secret. Specify these in the grafana config file. Example client_id = YOUR_GITHUB_APP_CLIENT_ID client_secret = YOUR_GITHUB_APP_CLIENT_SECRET scopes = user:email + team_ids = auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token allow_sign_up = false @@ -189,6 +190,21 @@ now login or signup with your github accounts. You may allow users to sign-up via github auth by setting allow_sign_up to true. When this option is set to true, any user successfully authenticating via github auth will be automatically signed up. +### team_ids +Require an active team membership for at least one of the given teams on GitHub. +If the authenticated user isn't a member of at least one the teams they will not +be able to register or authenticate with your Grafana instance. Example: + + [auth.github] + enabled = true + client_id = YOUR_GITHUB_APP_CLIENT_ID + client_secret = YOUR_GITHUB_APP_CLIENT_SECRET + scopes = user:email + team_ids = 150,300 + auth_url = https://github.com/login/oauth/authorize + token_url = https://github.com/login/oauth/access_token + allow_sign_up = false + ## [auth.google] You need to create a google project. You can do this in the [Google Developer Console](https://console.developers.google.com/project). When you create the project you will need to specify a callback URL. Specify this as callback: @@ -257,5 +273,3 @@ enabled. Counters are sent every 24 hours. Default value is `true`. ### google_analytics_ua_id If you want to track Grafana usage via Google analytics specify *your* Univeral Analytics ID here. By defualt this feature is disabled. - -