providers: iterate across all pages from /user/orgs github endpoint.

For some GHE instances where a user can have more than 100
organizations, traversing the other pages is important otherwise
oauth2_proxy will consider the user unauthorized. This change traverses
the list returned by the API to avoid that.

Update github provider tests to include this case.
This commit is contained in:
Mark Maglana 2017-07-04 14:36:21 -07:00 committed by Tanvir Alam
parent faff555c55
commit 882fcf0a01
2 changed files with 80 additions and 37 deletions

View File

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv"
"strings" "strings"
) )
@ -61,36 +62,51 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
Login string `json:"login"` Login string `json:"login"`
} }
params := url.Values{ type orgsPage []struct {
"limit": {"100"}, Login string `json:"login"`
} }
endpoint := &url.URL{ pn := 1
Scheme: p.ValidateURL.Scheme, for {
Host: p.ValidateURL.Host, params := url.Values{
Path: path.Join(p.ValidateURL.Path, "/user/orgs"), "limit": {"200"},
RawQuery: params.Encode(), "page": {strconv.Itoa(pn)},
} }
req, _ := http.NewRequest("GET", endpoint.String(), nil)
req.Header.Set("Accept", "application/vnd.github.v3+json")
req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false, err
}
body, err := ioutil.ReadAll(resp.Body) endpoint := &url.URL{
resp.Body.Close() Scheme: p.ValidateURL.Scheme,
if err != nil { Host: p.ValidateURL.Host,
return false, err Path: path.Join(p.ValidateURL.Path, "/user/orgs"),
} RawQuery: params.Encode(),
if resp.StatusCode != 200 { }
return false, fmt.Errorf( req, _ := http.NewRequest("GET", endpoint.String(), nil)
"got %d from %q %s", resp.StatusCode, endpoint.String(), body) req.Header.Set("Accept", "application/vnd.github.v3+json")
} req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false, err
}
if err := json.Unmarshal(body, &orgs); err != nil { body, err := ioutil.ReadAll(resp.Body)
return false, err resp.Body.Close()
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, fmt.Errorf(
"got %d from %q %s", resp.StatusCode, endpoint.String(), body)
}
var op orgsPage
if err := json.Unmarshal(body, &op); err != nil {
return false, err
}
if len(op) == 0 {
break
}
orgs = append(orgs, op...)
pn += 1
} }
var presentOrgs []string var presentOrgs []string
@ -118,7 +134,7 @@ func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
} }
params := url.Values{ params := url.Values{
"limit": {"100"}, "limit": {"200"},
} }
endpoint := &url.URL{ endpoint := &url.URL{

View File

@ -27,23 +27,32 @@ func testGitHubProvider(hostname string) *GitHubProvider {
return p return p
} }
func testGitHubBackend(payload string) *httptest.Server { func testGitHubBackend(payload []string) *httptest.Server {
pathToQueryMap := map[string]string{ pathToQueryMap := map[string][]string{
"/user": "", "/user": []string{""},
"/user/emails": "", "/user/emails": []string{""},
"/user/orgs": []string{"limit=200&page=1", "limit=200&page=2", "limit=200&page=3"},
} }
return httptest.NewServer(http.HandlerFunc( return httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) { func(w http.ResponseWriter, r *http.Request) {
url := r.URL url := r.URL
query, ok := pathToQueryMap[url.Path] query, ok := pathToQueryMap[url.Path]
validQuery := false
index := 0
for i, q := range query {
if q == url.RawQuery {
validQuery = true
index = i
}
}
if !ok { if !ok {
w.WriteHeader(404) w.WriteHeader(404)
} else if url.RawQuery != query { } else if !validQuery {
w.WriteHeader(404) w.WriteHeader(404)
} else { } else {
w.WriteHeader(200) w.WriteHeader(200)
w.Write([]byte(payload)) w.Write([]byte(payload[index]))
} }
})) }))
} }
@ -89,7 +98,7 @@ func TestGitHubProviderOverrides(t *testing.T) {
} }
func TestGitHubProviderGetEmailAddress(t *testing.T) { func TestGitHubProviderGetEmailAddress(t *testing.T) {
b := testGitHubBackend(`[ {"email": "michael.bland@gsa.gov", "primary": true} ]`) b := testGitHubBackend([]string{`[ {"email": "michael.bland@gsa.gov", "primary": true} ]`})
defer b.Close() defer b.Close()
bURL, _ := url.Parse(b.URL) bURL, _ := url.Parse(b.URL)
@ -101,10 +110,28 @@ func TestGitHubProviderGetEmailAddress(t *testing.T) {
assert.Equal(t, "michael.bland@gsa.gov", email) assert.Equal(t, "michael.bland@gsa.gov", email)
} }
func TestGitHubProviderGetEmailAddressWithOrg(t *testing.T) {
b := testGitHubBackend([]string{
`[ {"email": "michael.bland@gsa.gov", "primary": true, "login":"testorg"} ]`,
`[ {"email": "michael.bland1@gsa.gov", "primary": true, "login":"testorg1"} ]`,
`[ ]`,
})
defer b.Close()
bURL, _ := url.Parse(b.URL)
p := testGitHubProvider(bURL.Host)
p.Org = "testorg1"
session := &SessionState{AccessToken: "imaginary_access_token"}
email, err := p.GetEmailAddress(session)
assert.Equal(t, nil, err)
assert.Equal(t, "michael.bland@gsa.gov", email)
}
// Note that trying to trigger the "failed building request" case is not // Note that trying to trigger the "failed building request" case is not
// practical, since the only way it can fail is if the URL fails to parse. // practical, since the only way it can fail is if the URL fails to parse.
func TestGitHubProviderGetEmailAddressFailedRequest(t *testing.T) { func TestGitHubProviderGetEmailAddressFailedRequest(t *testing.T) {
b := testGitHubBackend("unused payload") b := testGitHubBackend([]string{"unused payload"})
defer b.Close() defer b.Close()
bURL, _ := url.Parse(b.URL) bURL, _ := url.Parse(b.URL)
@ -120,7 +147,7 @@ func TestGitHubProviderGetEmailAddressFailedRequest(t *testing.T) {
} }
func TestGitHubProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) { func TestGitHubProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
b := testGitHubBackend("{\"foo\": \"bar\"}") b := testGitHubBackend([]string{"{\"foo\": \"bar\"}"})
defer b.Close() defer b.Close()
bURL, _ := url.Parse(b.URL) bURL, _ := url.Parse(b.URL)
@ -133,7 +160,7 @@ func TestGitHubProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
} }
func TestGitHubProviderGetUserName(t *testing.T) { func TestGitHubProviderGetUserName(t *testing.T) {
b := testGitHubBackend(`{"email": "michael.bland@gsa.gov", "login": "mbland"}`) b := testGitHubBackend([]string{`{"email": "michael.bland@gsa.gov", "login": "mbland"}`})
defer b.Close() defer b.Close()
bURL, _ := url.Parse(b.URL) bURL, _ := url.Parse(b.URL)