Commit abed438d by Torkel Ödegaard

Merge pull request #2052 from indrekj/org-support

Add github organizations support
parents 0047ce06 b55d9350
...@@ -153,6 +153,7 @@ token_url = https://github.com/login/oauth/access_token ...@@ -153,6 +153,7 @@ token_url = https://github.com/login/oauth/access_token
api_url = https://api.github.com/user api_url = https://api.github.com/user
team_ids = team_ids =
allowed_domains = allowed_domains =
allowed_organizations =
#################################### Google Auth ########################## #################################### Google Auth ##########################
[auth.google] [auth.google]
......
...@@ -146,12 +146,13 @@ ...@@ -146,12 +146,13 @@
;allow_sign_up = false ;allow_sign_up = false
;client_id = some_id ;client_id = some_id
;client_secret = some_secret ;client_secret = some_secret
;scopes = user:email ;scopes = user:email,read:org
;auth_url = https://github.com/login/oauth/authorize ;auth_url = https://github.com/login/oauth/authorize
;token_url = https://github.com/login/oauth/access_token ;token_url = https://github.com/login/oauth/access_token
;api_url = https://api.github.com/user ;api_url = https://api.github.com/user
;team_ids = ;team_ids =
;allowed_domains = ;allowed_domains =
;allowed_organizations =
#################################### Google Auth ########################## #################################### Google Auth ##########################
[auth.google] [auth.google]
......
...@@ -48,6 +48,8 @@ func OAuthLogin(ctx *middleware.Context) { ...@@ -48,6 +48,8 @@ func OAuthLogin(ctx *middleware.Context) {
if err != nil { if err != nil {
if err == social.ErrMissingTeamMembership { if err == social.ErrMissingTeamMembership {
ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github team membership not fulfilled")) ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github team membership not fulfilled"))
} else if err == social.ErrMissingOrganizationMembership {
ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github organization membership not fulfilled"))
} else { } else {
ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err) ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err)
} }
......
...@@ -78,12 +78,14 @@ func NewOAuthService() { ...@@ -78,12 +78,14 @@ func NewOAuthService() {
if name == "github" { if name == "github" {
setting.OAuthService.GitHub = true setting.OAuthService.GitHub = true
teamIds := sec.Key("team_ids").Ints(",") teamIds := sec.Key("team_ids").Ints(",")
allowedOrganizations := sec.Key("allowed_organizations").Strings(" ")
SocialMap["github"] = &SocialGithub{ SocialMap["github"] = &SocialGithub{
Config: &config, Config: &config,
allowedDomains: info.AllowedDomains, allowedDomains: info.AllowedDomains,
apiUrl: info.ApiUrl, apiUrl: info.ApiUrl,
allowSignup: info.AllowSignup, allowSignup: info.AllowSignup,
teamIds: teamIds, teamIds: teamIds,
allowedOrganizations: allowedOrganizations,
} }
} }
...@@ -115,16 +117,21 @@ func isEmailAllowed(email string, allowedDomains []string) bool { ...@@ -115,16 +117,21 @@ func isEmailAllowed(email string, allowedDomains []string) bool {
type SocialGithub struct { type SocialGithub struct {
*oauth2.Config *oauth2.Config
allowedDomains []string allowedDomains []string
apiUrl string allowedOrganizations []string
allowSignup bool apiUrl string
teamIds []int allowSignup bool
teamIds []int
} }
var ( var (
ErrMissingTeamMembership = errors.New("User not a member of one of the required teams") ErrMissingTeamMembership = errors.New("User not a member of one of the required teams")
) )
var (
ErrMissingOrganizationMembership = errors.New("User not a member of one of the required organizations")
)
func (s *SocialGithub) Type() int { func (s *SocialGithub) Type() int {
return int(models.GITHUB) return int(models.GITHUB)
} }
...@@ -137,26 +144,100 @@ func (s *SocialGithub) IsSignupAllowed() bool { ...@@ -137,26 +144,100 @@ func (s *SocialGithub) IsSignupAllowed() bool {
return s.allowSignup return s.allowSignup
} }
func (s *SocialGithub) IsTeamMember(client *http.Client, username string, teamId int) bool { func (s *SocialGithub) IsTeamMember(client *http.Client) bool {
var data struct { if len(s.teamIds) == 0 {
Url string `json:"url"` return true
State string `json:"state"`
} }
membershipUrl := fmt.Sprintf("https://api.github.com/teams/%d/memberships/%s", teamId, username) teamMemberships, err := s.FetchTeamMemberships(client)
r, err := client.Get(membershipUrl)
if err != nil { if err != nil {
return false return false
} }
defer r.Body.Close() for _, teamId := range s.teamIds {
for _, membershipId := range teamMemberships {
if teamId == membershipId {
return true
}
}
}
if err = json.NewDecoder(r.Body).Decode(&data); err != nil { return false
}
func (s *SocialGithub) IsOrganizationMember(client *http.Client) bool {
if len(s.allowedOrganizations) == 0 {
return true
}
organizations, err := s.FetchOrganizations(client)
if err != nil {
return false return false
} }
active := data.State == "active" for _, allowedOrganization := range s.allowedOrganizations {
return active for _, organization := range organizations {
if organization == allowedOrganization {
return true
}
}
}
return false
}
func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error) {
type Record struct {
Id int `json:"id"`
}
membershipUrl := fmt.Sprintf("https://api.github.com/user/teams")
r, err := client.Get(membershipUrl)
if err != nil {
return nil, err
}
defer r.Body.Close()
var records []Record
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
return nil, err
}
var ids = make([]int, len(records))
for i, record := range records {
ids[i] = record.Id
}
return ids, nil
}
func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error) {
type Record struct {
Login string `json:"login"`
}
url := fmt.Sprintf("https://api.github.com/user/orgs")
r, err := client.Get(url)
if err != nil {
return nil, err
}
defer r.Body.Close()
var records []Record
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
return nil, err
}
var logins = make([]string, len(records))
for i, record := range records {
logins[i] = record.Login
}
return logins, nil
} }
func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
...@@ -185,17 +266,15 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { ...@@ -185,17 +266,15 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
Email: data.Email, Email: data.Email,
} }
if len(s.teamIds) > 0 { if !s.IsTeamMember(client) {
for _, teamId := range s.teamIds {
if s.IsTeamMember(client, data.Name, teamId) {
return userInfo, nil
}
}
return nil, ErrMissingTeamMembership return nil, ErrMissingTeamMembership
} else {
return userInfo, nil
} }
if !s.IsOrganizationMember(client) {
return nil, ErrMissingOrganizationMembership
}
return userInfo, nil
} }
// ________ .__ // ________ .__
......
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