mirror of https://github.com/vouch/vouch-proxy
200 lines
6.0 KiB
Go
200 lines
6.0 KiB
Go
/*
|
|
|
|
Copyright 2020 The Vouch Proxy Authors.
|
|
Use of this source code is governed by The MIT License (MIT) that
|
|
can be found in the LICENSE file. Software distributed under The
|
|
MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
|
|
OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
*/
|
|
|
|
package github
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"regexp"
|
|
"testing"
|
|
|
|
mockhttp "github.com/karupanerura/go-mock-http-response"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/vouch/vouch-proxy/pkg/cfg"
|
|
"github.com/vouch/vouch-proxy/pkg/domains"
|
|
"github.com/vouch/vouch-proxy/pkg/structs"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
type ReqMatcher func(*http.Request) bool
|
|
|
|
type FunResponsePair struct {
|
|
matcher ReqMatcher
|
|
response *mockhttp.ResponseMock
|
|
}
|
|
|
|
type Transport struct {
|
|
MockError error
|
|
}
|
|
|
|
func (c *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
if c.MockError != nil {
|
|
return nil, c.MockError
|
|
}
|
|
for _, p := range mockedResponses {
|
|
if p.matcher(req) {
|
|
requests = append(requests, req.URL.String())
|
|
return p.response.MakeResponse(req), nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func mockResponse(fun ReqMatcher, statusCode int, headers map[string]string, body []byte) {
|
|
mockedResponses = append(mockedResponses, FunResponsePair{matcher: fun, response: mockhttp.NewResponseMock(statusCode, headers, body)})
|
|
}
|
|
|
|
func regexMatcher(expr string) ReqMatcher {
|
|
return func(r *http.Request) bool {
|
|
matches, _ := regexp.Match(expr, []byte(r.URL.String()))
|
|
return matches
|
|
}
|
|
}
|
|
|
|
func urlEquals(value string) ReqMatcher {
|
|
return func(r *http.Request) bool {
|
|
return r.URL.String() == value
|
|
}
|
|
}
|
|
|
|
func assertURLCalled(t *testing.T, url string) {
|
|
found := false
|
|
for _, requestedURL := range requests {
|
|
if requestedURL == url {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "Expected %s to have been called, but got only %s", url, requests)
|
|
}
|
|
|
|
var (
|
|
user *structs.User
|
|
token = &oauth2.Token{AccessToken: "123"}
|
|
mockedResponses = []FunResponsePair{}
|
|
requests []string
|
|
client = &http.Client{Transport: &Transport{}}
|
|
)
|
|
|
|
func setUp() {
|
|
log = cfg.Logging.Logger
|
|
cfg.InitForTestPurposesWithProvider("github")
|
|
|
|
cfg.Cfg.AllowAllUsers = false
|
|
cfg.Cfg.WhiteList = make([]string, 0)
|
|
cfg.Cfg.TeamWhiteList = make([]string, 0)
|
|
cfg.Cfg.Domains = []string{"domain1"}
|
|
|
|
domains.Configure()
|
|
|
|
mockedResponses = []FunResponsePair{}
|
|
requests = make([]string, 0)
|
|
|
|
user = &structs.User{Username: "testuser", Email: "test@example.com"}
|
|
}
|
|
|
|
func TestGetTeamMembershipStateFromGitHubActive(t *testing.T) {
|
|
setUp()
|
|
mockResponse(regexMatcher(".*"), http.StatusOK, map[string]string{}, []byte("{\"state\": \"active\"}"))
|
|
|
|
isMember, err := getTeamMembershipStateFromGitHub(client, user, "org1", "team1", token)
|
|
|
|
assert.Nil(t, err)
|
|
assert.True(t, isMember)
|
|
}
|
|
|
|
func TestGetTeamMembershipStateFromGitHubInactive(t *testing.T) {
|
|
setUp()
|
|
mockResponse(regexMatcher(".*"), http.StatusOK, map[string]string{}, []byte("{\"state\": \"inactive\"}"))
|
|
|
|
isMember, err := getTeamMembershipStateFromGitHub(client, user, "org1", "team1", token)
|
|
|
|
assert.Nil(t, err)
|
|
assert.False(t, isMember)
|
|
}
|
|
|
|
func TestGetTeamMembershipStateFromGitHubNotAMember(t *testing.T) {
|
|
setUp()
|
|
mockResponse(regexMatcher(".*"), http.StatusNotFound, map[string]string{}, []byte(""))
|
|
|
|
isMember, err := getTeamMembershipStateFromGitHub(client, user, "org1", "team1", token)
|
|
|
|
assert.Nil(t, err)
|
|
assert.False(t, isMember)
|
|
}
|
|
|
|
func TestGetOrgMembershipStateFromGitHubNotFound(t *testing.T) {
|
|
setUp()
|
|
mockResponse(regexMatcher(".*"), http.StatusNotFound, map[string]string{}, []byte(""))
|
|
|
|
isMember, err := getOrgMembershipStateFromGitHub(client, user, "myorg", token)
|
|
|
|
assert.Nil(t, err)
|
|
assert.False(t, isMember)
|
|
|
|
expectedOrgMembershipURL := "https://api.github.com/orgs/myorg/members/" + user.Username + "?access_token=" + token.AccessToken
|
|
assertURLCalled(t, expectedOrgMembershipURL)
|
|
}
|
|
|
|
func TestGetOrgMembershipStateFromGitHubNoOrgAccess(t *testing.T) {
|
|
setUp()
|
|
location := "https://api.github.com/orgs/myorg/public_members/" + user.Username
|
|
|
|
mockResponse(regexMatcher(".*orgs/myorg/members.*"), http.StatusFound, map[string]string{"Location": location}, []byte(""))
|
|
mockResponse(regexMatcher(".*orgs/myorg/public_members.*"), http.StatusNoContent, map[string]string{}, []byte(""))
|
|
|
|
isMember, err := getOrgMembershipStateFromGitHub(client, user, "myorg", token)
|
|
|
|
assert.Nil(t, err)
|
|
assert.True(t, isMember)
|
|
|
|
expectedOrgMembershipURL := "https://api.github.com/orgs/myorg/members/" + user.Username + "?access_token=" + token.AccessToken
|
|
assertURLCalled(t, expectedOrgMembershipURL)
|
|
|
|
expectedOrgPublicMembershipURL := "https://api.github.com/orgs/myorg/public_members/" + user.Username
|
|
assertURLCalled(t, expectedOrgPublicMembershipURL)
|
|
}
|
|
|
|
func TestGetUserInfo(t *testing.T) {
|
|
setUp()
|
|
|
|
userInfoContent, _ := json.Marshal(structs.GitHubUser{
|
|
User: structs.User{
|
|
Username: "test",
|
|
CreatedOn: 123,
|
|
Email: "email@example.com",
|
|
ID: 1,
|
|
LastUpdate: 123,
|
|
Name: "name",
|
|
},
|
|
Login: "myusername",
|
|
Picture: "avatar-url",
|
|
})
|
|
mockResponse(urlEquals(cfg.GenOAuth.UserInfoURL+token.AccessToken), http.StatusOK, map[string]string{}, userInfoContent)
|
|
|
|
cfg.Cfg.TeamWhiteList = append(cfg.Cfg.TeamWhiteList, "myOtherOrg", "myorg/myteam")
|
|
|
|
mockResponse(regexMatcher(".*teams.*"), http.StatusOK, map[string]string{}, []byte("{\"state\": \"active\"}"))
|
|
mockResponse(regexMatcher(".*members.*"), http.StatusNoContent, map[string]string{}, []byte(""))
|
|
|
|
provider := Provider{PrepareTokensAndClient: func(_ *http.Request, _ *structs.PTokens, _ bool, opts ...oauth2.AuthCodeOption) (*http.Client, *oauth2.Token, error) {
|
|
return client, token, nil
|
|
}}
|
|
err := provider.GetUserInfo(nil, user, &structs.CustomClaims{}, &structs.PTokens{})
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, "myusername", user.Username)
|
|
assert.Equal(t, []string{"myOtherOrg", "myorg/myteam"}, user.TeamMemberships)
|
|
|
|
expectedTeamMembershipURL := "https://api.github.com/orgs/myorg/teams/myteam/memberships/myusername?access_token=" + token.AccessToken
|
|
assertURLCalled(t, expectedTeamMembershipURL)
|
|
}
|