2015-05-21 06:23:48 +03:00
package providers
import (
"encoding/json"
2015-06-06 21:15:43 +03:00
"fmt"
2015-05-21 06:23:48 +03:00
"io/ioutil"
2015-06-23 14:23:39 +03:00
"log"
2015-05-21 06:23:48 +03:00
"net/http"
"net/url"
)
type GitHubProvider struct {
* ProviderData
Org string
Team string
}
func NewGitHubProvider ( p * ProviderData ) * GitHubProvider {
p . ProviderName = "GitHub"
2015-11-09 02:47:44 +03:00
if p . LoginURL == nil || p . LoginURL . String ( ) == "" {
p . LoginURL = & url . URL {
2015-05-21 06:23:48 +03:00
Scheme : "https" ,
Host : "github.com" ,
Path : "/login/oauth/authorize" ,
}
}
2015-11-09 02:47:44 +03:00
if p . RedeemURL == nil || p . RedeemURL . String ( ) == "" {
p . RedeemURL = & url . URL {
2015-05-21 06:23:48 +03:00
Scheme : "https" ,
Host : "github.com" ,
Path : "/login/oauth/access_token" ,
}
}
2015-11-09 02:47:44 +03:00
if p . ValidateURL == nil || p . ValidateURL . String ( ) == "" {
p . ValidateURL = & url . URL {
2015-05-21 06:23:48 +03:00
Scheme : "https" ,
Host : "api.github.com" ,
Path : "/user/emails" ,
}
}
if p . Scope == "" {
p . Scope = "user:email"
}
return & GitHubProvider { ProviderData : p }
}
func ( p * GitHubProvider ) SetOrgTeam ( org , team string ) {
p . Org = org
p . Team = team
if org != "" || team != "" {
p . Scope += " read:org"
}
}
2015-06-06 21:03:59 +03:00
func ( p * GitHubProvider ) hasOrg ( accessToken string ) ( bool , error ) {
// https://developer.github.com/v3/orgs/#list-your-organizations
var orgs [ ] struct {
Login string ` json:"login" `
}
params := url . Values {
"access_token" : { accessToken } ,
2015-06-06 21:15:43 +03:00
"limit" : { "100" } ,
2015-06-06 21:03:59 +03:00
}
2016-01-18 20:33:25 +03:00
endpoint := p . ValidateURL . Scheme + "://" + p . ValidateURL . Host + "/user/orgs?" + params . Encode ( )
2015-06-06 21:15:43 +03:00
req , _ := http . NewRequest ( "GET" , endpoint , nil )
2015-07-24 23:10:10 +03:00
req . Header . Set ( "Accept" , "application/vnd.github.v3+json" )
2015-06-06 21:03:59 +03:00
resp , err := http . DefaultClient . Do ( req )
if err != nil {
return false , err
}
body , err := ioutil . ReadAll ( resp . Body )
resp . Body . Close ( )
if err != nil {
return false , err
}
2015-06-06 21:15:43 +03:00
if resp . StatusCode != 200 {
return false , fmt . Errorf ( "got %d from %q %s" , resp . StatusCode , endpoint , body )
}
2015-06-06 21:03:59 +03:00
if err := json . Unmarshal ( body , & orgs ) ; err != nil {
return false , err
}
2015-07-24 23:10:10 +03:00
var presentOrgs [ ] string
2015-06-06 21:03:59 +03:00
for _ , org := range orgs {
if p . Org == org . Login {
2015-07-24 23:10:10 +03:00
log . Printf ( "Found Github Organization: %q" , org . Login )
2015-06-06 21:03:59 +03:00
return true , nil
}
2015-07-24 23:10:10 +03:00
presentOrgs = append ( presentOrgs , org . Login )
2015-06-06 21:03:59 +03:00
}
2015-07-24 23:10:10 +03:00
log . Printf ( "Missing Organization:%q in %v" , p . Org , presentOrgs )
2015-06-06 21:03:59 +03:00
return false , nil
}
2015-05-21 06:23:48 +03:00
func ( p * GitHubProvider ) hasOrgAndTeam ( accessToken string ) ( bool , error ) {
2015-06-06 21:03:59 +03:00
// https://developer.github.com/v3/orgs/teams/#list-user-teams
2015-05-21 06:23:48 +03:00
var teams [ ] struct {
Name string ` json:"name" `
Slug string ` json:"slug" `
Org struct {
Login string ` json:"login" `
} ` json:"organization" `
}
params := url . Values {
"access_token" : { accessToken } ,
2015-06-06 21:15:43 +03:00
"limit" : { "100" } ,
2015-05-21 06:23:48 +03:00
}
2016-01-18 20:33:25 +03:00
endpoint := p . ValidateURL . Scheme + "://" + p . ValidateURL . Host + "/user/teams?" + params . Encode ( )
2015-06-06 21:15:43 +03:00
req , _ := http . NewRequest ( "GET" , endpoint , nil )
2015-07-24 23:10:10 +03:00
req . Header . Set ( "Accept" , "application/vnd.github.v3+json" )
2015-05-21 06:23:48 +03:00
resp , err := http . DefaultClient . Do ( req )
if err != nil {
return false , err
}
body , err := ioutil . ReadAll ( resp . Body )
resp . Body . Close ( )
if err != nil {
return false , err
}
2015-06-06 21:15:43 +03:00
if resp . StatusCode != 200 {
return false , fmt . Errorf ( "got %d from %q %s" , resp . StatusCode , endpoint , body )
}
2015-05-21 06:23:48 +03:00
if err := json . Unmarshal ( body , & teams ) ; err != nil {
2015-06-06 21:15:43 +03:00
return false , fmt . Errorf ( "%s unmarshaling %s" , err , body )
2015-05-21 06:23:48 +03:00
}
2015-07-24 23:10:10 +03:00
var hasOrg bool
presentOrgs := make ( map [ string ] bool )
var presentTeams [ ] string
2015-05-21 06:23:48 +03:00
for _ , team := range teams {
2015-07-24 23:10:10 +03:00
presentOrgs [ team . Org . Login ] = true
2015-05-21 06:23:48 +03:00
if p . Org == team . Org . Login {
2015-07-24 23:10:10 +03:00
hasOrg = true
if p . Team == team . Slug {
log . Printf ( "Found Github Organization:%q Team:%q (Name:%q)" , team . Org . Login , team . Slug , team . Name )
2015-05-21 06:23:48 +03:00
return true , nil
}
2015-07-24 23:10:10 +03:00
presentTeams = append ( presentTeams , team . Slug )
}
}
if hasOrg {
log . Printf ( "Missing Team:%q from Org:%q in teams: %v" , p . Team , p . Org , presentTeams )
} else {
var allOrgs [ ] string
for org , _ := range presentOrgs {
allOrgs = append ( allOrgs , org )
2015-05-21 06:23:48 +03:00
}
2015-07-24 23:10:10 +03:00
log . Printf ( "Missing Organization:%q in %#v" , p . Org , allOrgs )
2015-05-21 06:23:48 +03:00
}
return false , nil
}
2015-06-23 14:23:39 +03:00
func ( p * GitHubProvider ) GetEmailAddress ( s * SessionState ) ( string , error ) {
2015-05-21 06:23:48 +03:00
var emails [ ] struct {
Email string ` json:"email" `
Primary bool ` json:"primary" `
}
// if we require an Org or Team, check that first
2015-06-06 21:03:59 +03:00
if p . Org != "" {
if p . Team != "" {
2015-06-23 14:23:39 +03:00
if ok , err := p . hasOrgAndTeam ( s . AccessToken ) ; err != nil || ! ok {
2015-06-06 21:03:59 +03:00
return "" , err
}
} else {
2015-06-23 14:23:39 +03:00
if ok , err := p . hasOrg ( s . AccessToken ) ; err != nil || ! ok {
2015-06-06 21:03:59 +03:00
return "" , err
}
2015-05-21 06:23:48 +03:00
}
}
2015-06-06 21:03:59 +03:00
params := url . Values {
2015-06-23 14:23:39 +03:00
"access_token" : { s . AccessToken } ,
2015-06-06 21:03:59 +03:00
}
2016-01-18 20:33:25 +03:00
endpoint := p . ValidateURL . Scheme + "://" + p . ValidateURL . Host + p . ValidateURL . Path + "?" + params . Encode ( )
2015-06-06 21:15:43 +03:00
resp , err := http . DefaultClient . Get ( endpoint )
2015-05-21 06:23:48 +03:00
if err != nil {
return "" , err
}
2015-06-23 14:23:39 +03:00
body , err := ioutil . ReadAll ( resp . Body )
2015-05-21 06:23:48 +03:00
resp . Body . Close ( )
if err != nil {
return "" , err
}
2015-06-23 14:23:39 +03:00
2015-06-06 21:15:43 +03:00
if resp . StatusCode != 200 {
return "" , fmt . Errorf ( "got %d from %q %s" , resp . StatusCode , endpoint , body )
2015-06-23 14:23:39 +03:00
} else {
log . Printf ( "got %d from %q %s" , resp . StatusCode , endpoint , body )
2015-06-06 21:15:43 +03:00
}
2015-05-21 06:23:48 +03:00
if err := json . Unmarshal ( body , & emails ) ; err != nil {
2015-06-06 21:15:43 +03:00
return "" , fmt . Errorf ( "%s unmarshaling %s" , err , body )
2015-05-21 06:23:48 +03:00
}
for _ , email := range emails {
if email . Primary {
return email . Email , nil
}
}
2015-07-24 23:23:19 +03:00
return "" , nil
2015-05-21 06:23:48 +03:00
}