mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-09-29 19:21:13 +02:00
fix gitea token validation by allowing custom validation url and extracting the proper base api url for github cloud, github enterprise and gitea (#2194)
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testGiteaProvider(hostname string, opts options.GitHubOptions) *GitHubProvider {
|
||||
p := NewGitHubProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "Gitea",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{Path: "/api/v1/user/emails"},
|
||||
Scope: ""},
|
||||
opts)
|
||||
p.ProviderName = "Gitea"
|
||||
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
updateURL(p.Data().RedeemURL, hostname)
|
||||
updateURL(p.Data().ProfileURL, hostname)
|
||||
updateURL(p.Data().ValidateURL, hostname)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func testGiteaBackend(payloads map[string][]string) *httptest.Server {
|
||||
pathToQueryMap := map[string][]string{
|
||||
"/api/v1/repos/oauth2-proxy/oauth2-proxy": {""},
|
||||
"/api/v1/repos/oauth2-proxy/oauth2-proxy/collaborators/mbland": {""},
|
||||
"/api/v1/user": {""},
|
||||
"/api/v1/user/emails": {""},
|
||||
"/api/v1/user/orgs": {"page=1&per_page=100", "page=2&per_page=100", "page=3&per_page=100"},
|
||||
}
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
query, ok := pathToQueryMap[r.URL.Path]
|
||||
validQuery := false
|
||||
index := 0
|
||||
for i, q := range query {
|
||||
if q == r.URL.RawQuery {
|
||||
validQuery = true
|
||||
index = i
|
||||
}
|
||||
}
|
||||
payload := []string{}
|
||||
if ok && validQuery {
|
||||
payload, ok = payloads[r.URL.Path]
|
||||
}
|
||||
if !ok {
|
||||
w.WriteHeader(404)
|
||||
} else if !validQuery {
|
||||
w.WriteHeader(404)
|
||||
} else if payload[index] == "" {
|
||||
w.WriteHeader(204)
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(payload[index]))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func TestGiteaProvider_ValidateSessionWithBaseUrl(t *testing.T) {
|
||||
b := testGiteaBackend(map[string][]string{})
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGiteaProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
|
||||
valid := p.ValidateSession(context.Background(), session)
|
||||
assert.False(t, valid)
|
||||
}
|
||||
|
||||
func TestGiteaProvider_ValidateSessionWithUserEmails(t *testing.T) {
|
||||
b := testGiteaBackend(map[string][]string{
|
||||
"/api/v1/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||
})
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGiteaProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
|
||||
valid := p.ValidateSession(context.Background(), session)
|
||||
assert.True(t, valid)
|
||||
}
|
||||
+28
-37
@@ -89,6 +89,27 @@ func makeGitHubHeader(accessToken string) http.Header {
|
||||
return makeAuthorizationHeader(tokenTypeToken, accessToken, extraHeaders)
|
||||
}
|
||||
|
||||
func (p *GitHubProvider) makeGitHubAPIEndpoint(endpoint string, params *url.Values) *url.URL {
|
||||
basePath := p.ValidateURL.Path
|
||||
|
||||
re := regexp.MustCompile(`^/api/v\d+`)
|
||||
match := re.FindString(p.ValidateURL.Path)
|
||||
if match != "" {
|
||||
basePath = match
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
params = &url.Values{}
|
||||
}
|
||||
|
||||
return &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(basePath, endpoint),
|
||||
RawQuery: params.Encode(),
|
||||
}
|
||||
}
|
||||
|
||||
// setOrgTeam adds GitHub org reading parameters to the OAuth2 scope
|
||||
func (p *GitHubProvider) setOrgTeam(org, team string) {
|
||||
p.Org = org
|
||||
@@ -141,12 +162,7 @@ func (p *GitHubProvider) hasOrg(ctx context.Context, accessToken string) (bool,
|
||||
"page": {strconv.Itoa(pn)},
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/user/orgs"),
|
||||
RawQuery: params.Encode(),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/user/orgs", ¶ms)
|
||||
|
||||
var op orgsPage
|
||||
err := requests.New(endpoint.String()).
|
||||
@@ -206,12 +222,7 @@ func (p *GitHubProvider) hasOrgAndTeam(ctx context.Context, accessToken string)
|
||||
"page": {strconv.Itoa(pn)},
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/user/teams"),
|
||||
RawQuery: params.Encode(),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/user/teams", ¶ms)
|
||||
|
||||
// bodyclose cannot detect that the body is being closed later in requests.Into,
|
||||
// so have to skip the linting for the next line.
|
||||
@@ -309,11 +320,7 @@ func (p *GitHubProvider) hasRepo(ctx context.Context, accessToken string) (bool,
|
||||
Private bool `json:"private"`
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/repos/", p.Repo),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/repos/"+p.Repo, nil)
|
||||
|
||||
var repo repository
|
||||
err := requests.New(endpoint.String()).
|
||||
@@ -338,11 +345,7 @@ func (p *GitHubProvider) hasUser(ctx context.Context, accessToken string) (bool,
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/user"),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/user", nil)
|
||||
|
||||
err := requests.New(endpoint.String()).
|
||||
WithContext(ctx).
|
||||
@@ -362,11 +365,7 @@ func (p *GitHubProvider) hasUser(ctx context.Context, accessToken string) (bool,
|
||||
func (p *GitHubProvider) isCollaborator(ctx context.Context, username, accessToken string) (bool, error) {
|
||||
//https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/repos/", p.Repo, "/collaborators/", username),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/repos/"+p.Repo+"/collaborators/"+username, nil)
|
||||
result := requests.New(endpoint.String()).
|
||||
WithContext(ctx).
|
||||
WithHeaders(makeGitHubHeader(accessToken)).
|
||||
@@ -426,11 +425,7 @@ func (p *GitHubProvider) getEmail(ctx context.Context, s *sessions.SessionState)
|
||||
}
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/user/emails"),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/user/emails", nil)
|
||||
err := requests.New(endpoint.String()).
|
||||
WithContext(ctx).
|
||||
WithHeaders(makeGitHubHeader(s.AccessToken)).
|
||||
@@ -459,11 +454,7 @@ func (p *GitHubProvider) getUser(ctx context.Context, s *sessions.SessionState)
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
endpoint := &url.URL{
|
||||
Scheme: p.ValidateURL.Scheme,
|
||||
Host: p.ValidateURL.Host,
|
||||
Path: path.Join(p.ValidateURL.Path, "/user"),
|
||||
}
|
||||
endpoint := p.makeGitHubAPIEndpoint("/user", nil)
|
||||
|
||||
err := requests.New(endpoint.String()).
|
||||
WithContext(ctx).
|
||||
|
||||
@@ -34,11 +34,15 @@ func testGitHubProvider(hostname string, opts options.GitHubOptions) *GitHubProv
|
||||
|
||||
func testGitHubBackend(payloads map[string][]string) *httptest.Server {
|
||||
pathToQueryMap := map[string][]string{
|
||||
"/repos/oauth2-proxy/oauth2-proxy": {""},
|
||||
"/": {""},
|
||||
"/repos/oauth2-proxy/oauth2-proxy": {""},
|
||||
"/repos/oauth2-proxy/oauth2-proxy/collaborators/mbland": {""},
|
||||
"/user": {""},
|
||||
"/user/emails": {""},
|
||||
"/user/orgs": {"page=1&per_page=100", "page=2&per_page=100", "page=3&per_page=100"},
|
||||
// GitHub Enterprise Server API
|
||||
"/api/v3": {""},
|
||||
"/api/v3/user/emails": {""},
|
||||
}
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
@@ -75,10 +79,10 @@ func TestNewGitHubProvider(t *testing.T) {
|
||||
// Test that defaults are set when calling for a new provider with nothing set
|
||||
providerData := NewGitHubProvider(&ProviderData{}, options.GitHubOptions{}).Data()
|
||||
g.Expect(providerData.ProviderName).To(Equal("GitHub"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://github.com/login/oauth/authorize"))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://github.com/login/oauth/access_token"))
|
||||
g.Expect(providerData.LoginURL.String()).To(Equal(githubDefaultLoginURL.String()))
|
||||
g.Expect(providerData.RedeemURL.String()).To(Equal(githubDefaultRedeemURL.String()))
|
||||
g.Expect(providerData.ProfileURL.String()).To(Equal(""))
|
||||
g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.github.com/"))
|
||||
g.Expect(providerData.ValidateURL.String()).To(Equal(githubDefaultValidateURL.String()))
|
||||
g.Expect(providerData.Scope).To(Equal("user:email"))
|
||||
}
|
||||
|
||||
@@ -440,3 +444,50 @@ func TestGitHubProvider_getEmailWithUsernameAndNoAccessToPrivateRepo(t *testing.
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "michael.bland@gsa.gov", session.Email)
|
||||
}
|
||||
|
||||
func TestGitHubProvider_ValidateSessionWithBaseUrl(t *testing.T) {
|
||||
b := testGitHubBackend(map[string][]string{
|
||||
"/": {`[]`},
|
||||
})
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
|
||||
valid := p.ValidateSession(context.Background(), session)
|
||||
assert.True(t, valid)
|
||||
}
|
||||
|
||||
func TestGitHubProvider_ValidateSessionWithEnterpriseBaseUrl(t *testing.T) {
|
||||
b := testGitHubBackend(map[string][]string{
|
||||
"/api/v3": {`[]`},
|
||||
})
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
p.ValidateURL.Path = "/api/v3"
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
|
||||
valid := p.ValidateSession(context.Background(), session)
|
||||
assert.True(t, valid)
|
||||
}
|
||||
|
||||
func TestGitHubProvider_ValidateSessionWithUserEmails(t *testing.T) {
|
||||
b := testGitHubBackend(map[string][]string{
|
||||
"/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||
})
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testGitHubProvider(bURL.Host, options.GitHubOptions{})
|
||||
p.ValidateURL.Path = "/user/emails"
|
||||
|
||||
session := CreateAuthorizedSession()
|
||||
|
||||
valid := p.ValidateSession(context.Background(), session)
|
||||
assert.True(t, valid)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user