Merge pull request #274 from toshi-miura/ap-gh-pagination-with-lastpage
Added pagination support for /user/teams github api with link header .
This commit is contained in:
		
						commit
						9fdb8acec6
					
				|  | @ -9,10 +9,12 @@ | ||||||
| - [#258](https://github.com/pusher/oauth2_proxy/pull/258) Add IDToken for Azure provider | - [#258](https://github.com/pusher/oauth2_proxy/pull/258) Add IDToken for Azure provider | ||||||
|   - This PR adds the IDToken into the session for the Azure provider allowing requests to a backend to be identified as a specific user. As a consequence, if you are using a cookie to store the session the cookie will now exceed the 4kb size limit and be split into multiple cookies. This can cause problems when using nginx as a proxy, resulting in no cookie being passed at all. Either increase the proxy_buffer_size in nginx or implement the redis session storage (see https://pusher.github.io/oauth2_proxy/configuration#redis-storage) |   - This PR adds the IDToken into the session for the Azure provider allowing requests to a backend to be identified as a specific user. As a consequence, if you are using a cookie to store the session the cookie will now exceed the 4kb size limit and be split into multiple cookies. This can cause problems when using nginx as a proxy, resulting in no cookie being passed at all. Either increase the proxy_buffer_size in nginx or implement the redis session storage (see https://pusher.github.io/oauth2_proxy/configuration#redis-storage) | ||||||
| - [#286](https://github.com/pusher/oauth2_proxy/pull/286) Requests.go updated with useful error messages (@biotom) | - [#286](https://github.com/pusher/oauth2_proxy/pull/286) Requests.go updated with useful error messages (@biotom) | ||||||
|  | - [#274](https://github.com/pusher/oauth2_proxy/pull/274) Supports many github teams with api pagination support (@toshi-miura, @apratina) | ||||||
| - [#302](https://github.com/pusher/oauth2_proxy/pull/302) Rewrite dist script (@syscll) | - [#302](https://github.com/pusher/oauth2_proxy/pull/302) Rewrite dist script (@syscll) | ||||||
| - [#304](https://github.com/pusher/oauth2_proxy/pull/304) Add new Logo! :tada: (@JoelSpeed) | - [#304](https://github.com/pusher/oauth2_proxy/pull/304) Add new Logo! :tada: (@JoelSpeed) | ||||||
| - [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze) | - [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze) | ||||||
| - [#309](https://github.com/pusher/oauth2_proxy/pull/309) Added support for custom CA when connecting to Redis cache | - [#309](https://github.com/pusher/oauth2_proxy/pull/309) Added support for custom CA when connecting to Redis cache | ||||||
|  | 
 | ||||||
| # v4.0.0 | # v4.0.0 | ||||||
| 
 | 
 | ||||||
| - [#248](https://github.com/pusher/oauth2_proxy/pull/248) Fix issue with X-Auth-Request-Redirect header being ignored | - [#248](https://github.com/pusher/oauth2_proxy/pull/248) Fix issue with X-Auth-Request-Redirect header being ignored | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"regexp" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | @ -75,8 +76,8 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) { | ||||||
| 	pn := 1 | 	pn := 1 | ||||||
| 	for { | 	for { | ||||||
| 		params := url.Values{ | 		params := url.Values{ | ||||||
| 			"limit": {"200"}, | 			"per_page": {"100"}, | ||||||
| 			"page":  {strconv.Itoa(pn)}, | 			"page":     {strconv.Itoa(pn)}, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		endpoint := &url.URL{ | 		endpoint := &url.URL{ | ||||||
|  | @ -139,36 +140,90 @@ func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) { | ||||||
| 		} `json:"organization"` | 		} `json:"organization"` | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	params := url.Values{ | 	type teamsPage []struct { | ||||||
| 		"limit": {"200"}, | 		Name string `json:"name"` | ||||||
|  | 		Slug string `json:"slug"` | ||||||
|  | 		Org  struct { | ||||||
|  | 			Login string `json:"login"` | ||||||
|  | 		} `json:"organization"` | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	endpoint := &url.URL{ | 	pn := 1 | ||||||
| 		Scheme:   p.ValidateURL.Scheme, | 	last := 0 | ||||||
| 		Host:     p.ValidateURL.Host, | 	for { | ||||||
| 		Path:     path.Join(p.ValidateURL.Path, "/user/teams"), | 		params := url.Values{ | ||||||
| 		RawQuery: params.Encode(), | 			"per_page": {"100"}, | ||||||
| 	} | 			"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/teams"), | ||||||
| 	} | 			RawQuery: params.Encode(), | ||||||
| 	if resp.StatusCode != 200 { | 		} | ||||||
| 		return false, fmt.Errorf( |  | ||||||
| 			"got %d from %q %s", resp.StatusCode, endpoint.String(), body) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if err := json.Unmarshal(body, &teams); err != nil { | 		req, _ := http.NewRequest("GET", endpoint.String(), nil) | ||||||
| 		return false, fmt.Errorf("%s unmarshaling %s", err, 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 last == 0 { | ||||||
|  | 			// link header may not be obtained
 | ||||||
|  | 			// When paging is not required and all data can be retrieved with a single call
 | ||||||
|  | 
 | ||||||
|  | 			// Conditions for obtaining the link header.
 | ||||||
|  | 			// 1. When paging is required (Example: When the data size is 100 and the page size is 99 or less)
 | ||||||
|  | 			// 2. When it exceeds the paging frame (Example: When there is only 10 records but the second page is called with a page size of 100)
 | ||||||
|  | 
 | ||||||
|  | 			// link header at not last page
 | ||||||
|  | 			// <https://api.github.com/user/teams?page=1&per_page=100>; rel="prev", <https://api.github.com/user/teams?page=1&per_page=100>; rel="last", <https://api.github.com/user/teams?page=1&per_page=100>; rel="first"
 | ||||||
|  | 			// link header at last page (doesn't exist last info)
 | ||||||
|  | 			// <https://api.github.com/user/teams?page=3&per_page=10>; rel="prev", <https://api.github.com/user/teams?page=1&per_page=10>; rel="first"
 | ||||||
|  | 
 | ||||||
|  | 			link := resp.Header.Get("Link") | ||||||
|  | 			rep1 := regexp.MustCompile(`(?s).*\<https://api.github.com/user/teams\?page=(.)&per_page=[0-9]+\>; rel="last".*`) | ||||||
|  | 			i, converr := strconv.Atoi(rep1.ReplaceAllString(link, "$1")) | ||||||
|  | 
 | ||||||
|  | 			// If the last page cannot be taken from the link in the http header, the last variable remains zero
 | ||||||
|  | 			if converr == nil { | ||||||
|  | 				last = i | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 		if err != nil { | ||||||
|  | 			resp.Body.Close() | ||||||
|  | 			return false, err | ||||||
|  | 		} | ||||||
|  | 		resp.Body.Close() | ||||||
|  | 
 | ||||||
|  | 		if resp.StatusCode != 200 { | ||||||
|  | 			return false, fmt.Errorf( | ||||||
|  | 				"got %d from %q %s", resp.StatusCode, endpoint.String(), body) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var tp teamsPage | ||||||
|  | 		if err := json.Unmarshal(body, &tp); err != nil { | ||||||
|  | 			return false, fmt.Errorf("%s unmarshaling %s", err, body) | ||||||
|  | 		} | ||||||
|  | 		if len(tp) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		teams = append(teams, tp...) | ||||||
|  | 
 | ||||||
|  | 		if pn == last { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		if last == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		pn++ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var hasOrg bool | 	var hasOrg bool | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ func testGitHubBackend(payload []string) *httptest.Server { | ||||||
| 	pathToQueryMap := map[string][]string{ | 	pathToQueryMap := map[string][]string{ | ||||||
| 		"/user":        {""}, | 		"/user":        {""}, | ||||||
| 		"/user/emails": {""}, | 		"/user/emails": {""}, | ||||||
| 		"/user/orgs":   {"limit=200&page=1", "limit=200&page=2", "limit=200&page=3"}, | 		"/user/orgs":   {"page=1&per_page=100", "page=2&per_page=100", "page=3&per_page=100"}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return httptest.NewServer(http.HandlerFunc( | 	return httptest.NewServer(http.HandlerFunc( | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue