From 7b416b5a8e5c364986a5f6ed1d310c740d5f4359 Mon Sep 17 00:00:00 2001 From: Nandor Kracser Date: Wed, 1 May 2019 20:13:44 +0200 Subject: [PATCH] gitlab: add tests --- connector/gitlab/gitlab.go | 13 +- connector/gitlab/gitlab_test.go | 220 ++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 connector/gitlab/gitlab_test.go diff --git a/connector/gitlab/gitlab.go b/connector/gitlab/gitlab.go index fd932142..2c031712 100644 --- a/connector/gitlab/gitlab.go +++ b/connector/gitlab/gitlab.go @@ -74,6 +74,7 @@ type gitlabConnector struct { clientID string clientSecret string logger log.Logger + httpClient *http.Client } func (c *gitlabConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config { @@ -118,7 +119,11 @@ func (c *gitlabConnector) HandleCallback(s connector.Scopes, r *http.Request) (i } oauth2Config := c.oauth2Config(s) + ctx := r.Context() + if c.httpClient != nil { + ctx = context.WithValue(r.Context(), oauth2.HTTPClient, c.httpClient) + } token, err := oauth2Config.Exchange(ctx, q.Get("code")) if err != nil { @@ -226,6 +231,10 @@ func (c *gitlabConnector) user(ctx context.Context, client *http.Client) (gitlab return u, nil } +type userInfo struct { + Groups []string +} + // userGroups queries the GitLab API for group membership. // // The HTTP passed client is expected to be constructed by the golang.org/x/oauth2 package, @@ -249,9 +258,7 @@ func (c *gitlabConnector) userGroups(ctx context.Context, client *http.Client) ( } return nil, fmt.Errorf("%s: %s", resp.Status, body) } - u := struct { - Groups []string - }{} + var u userInfo if err := json.NewDecoder(resp.Body).Decode(&u); err != nil { return nil, fmt.Errorf("failed to decode response: %v", err) } diff --git a/connector/gitlab/gitlab_test.go b/connector/gitlab/gitlab_test.go new file mode 100644 index 00000000..4be5e0f2 --- /dev/null +++ b/connector/gitlab/gitlab_test.go @@ -0,0 +1,220 @@ +package gitlab + +import ( + "context" + "crypto/tls" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + "github.com/dexidp/dex/connector" +) + +func TestUserGroups(t *testing.T) { + s := newTestServer(map[string]interface{}{ + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1", "team-2"}, + }, + }) + defer s.Close() + + c := gitlabConnector{baseURL: s.URL} + groups, err := c.getGroups(context.Background(), newClient(), true, "joebloggs") + + expectNil(t, err) + expectEquals(t, groups, []string{ + "team-1", + "team-2", + }) +} + +func TestUserGroupsWithFiltering(t *testing.T) { + s := newTestServer(map[string]interface{}{ + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1", "team-2"}, + }, + }) + defer s.Close() + + c := gitlabConnector{baseURL: s.URL, groups: []string{"team-1"}} + groups, err := c.getGroups(context.Background(), newClient(), true, "joebloggs") + + expectNil(t, err) + expectEquals(t, groups, []string{ + "team-1", + }) +} + +func TestUserGroupsWithoutOrgs(t *testing.T) { + s := newTestServer(map[string]interface{}{ + "/oauth/userinfo": userInfo{ + Groups: []string{}, + }, + }) + defer s.Close() + + c := gitlabConnector{baseURL: s.URL} + groups, err := c.getGroups(context.Background(), newClient(), true, "joebloggs") + + expectNil(t, err) + expectEquals(t, len(groups), 0) +} + +// tests that the email is used as their username when they have no username set +func TestUsernameIncludedInFederatedIdentity(t *testing.T) { + + s := newTestServer(map[string]interface{}{ + "/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678}, + "/oauth/token": map[string]interface{}{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", + "expires_in": "30", + }, + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1"}, + }, + }) + defer s.Close() + + hostURL, err := url.Parse(s.URL) + expectNil(t, err) + + req, err := http.NewRequest("GET", hostURL.String(), nil) + expectNil(t, err) + + c := gitlabConnector{baseURL: s.URL, httpClient: newClient()} + identity, err := c.HandleCallback(connector.Scopes{Groups: false}, req) + + expectNil(t, err) + expectEquals(t, identity.Username, "some@email.com") + expectEquals(t, identity.UserID, "12345678") + expectEquals(t, 0, len(identity.Groups)) + + c = gitlabConnector{baseURL: s.URL, httpClient: newClient()} + identity, err = c.HandleCallback(connector.Scopes{Groups: true}, req) + + expectNil(t, err) + expectEquals(t, identity.Username, "some@email.com") + expectEquals(t, identity.UserID, "12345678") + expectEquals(t, identity.Groups, []string{"team-1"}) +} + +func TestLoginUsedAsIDWhenConfigured(t *testing.T) { + + s := newTestServer(map[string]interface{}{ + "/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs"}, + "/oauth/token": map[string]interface{}{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", + "expires_in": "30", + }, + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1"}, + }, + }) + defer s.Close() + + hostURL, err := url.Parse(s.URL) + expectNil(t, err) + + req, err := http.NewRequest("GET", hostURL.String(), nil) + expectNil(t, err) + + c := gitlabConnector{baseURL: s.URL, httpClient: newClient()} + identity, err := c.HandleCallback(connector.Scopes{Groups: true}, req) + + expectNil(t, err) + expectEquals(t, identity.UserID, "12345678") + expectEquals(t, identity.Username, "Joe Bloggs") +} + +func TestLoginWithTeamWhitelisted(t *testing.T) { + + s := newTestServer(map[string]interface{}{ + "/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs"}, + "/oauth/token": map[string]interface{}{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", + "expires_in": "30", + }, + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1"}, + }, + }) + defer s.Close() + + hostURL, err := url.Parse(s.URL) + expectNil(t, err) + + req, err := http.NewRequest("GET", hostURL.String(), nil) + expectNil(t, err) + + c := gitlabConnector{baseURL: s.URL, httpClient: newClient(), groups: []string{"team-1"}} + identity, err := c.HandleCallback(connector.Scopes{Groups: true}, req) + + expectNil(t, err) + expectEquals(t, identity.UserID, "12345678") + expectEquals(t, identity.Username, "Joe Bloggs") +} + +func TestLoginWithTeamNonWhitelisted(t *testing.T) { + + s := newTestServer(map[string]interface{}{ + "/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs", Username: "joebloggs"}, + "/oauth/token": map[string]interface{}{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", + "expires_in": "30", + }, + "/oauth/userinfo": userInfo{ + Groups: []string{"team-1"}, + }, + }) + defer s.Close() + + hostURL, err := url.Parse(s.URL) + expectNil(t, err) + + req, err := http.NewRequest("GET", hostURL.String(), nil) + expectNil(t, err) + + c := gitlabConnector{baseURL: s.URL, httpClient: newClient(), groups: []string{"team-2"}} + _, err = c.HandleCallback(connector.Scopes{Groups: true}, req) + + expectNotNil(t, err, "HandleCallback error") + expectEquals(t, err.Error(), "gitlab: get groups: gitlab: user \"joebloggs\" is not in any of the required groups") +} + +func newTestServer(responses map[string]interface{}) *httptest.Server { + var s *httptest.Server + s = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := responses[r.RequestURI] + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + })) + return s +} + +func newClient() *http.Client { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + return &http.Client{Transport: tr} +} + +func expectNil(t *testing.T, a interface{}) { + if a != nil { + t.Errorf("Expected %+v to equal nil", a) + } +} + +func expectNotNil(t *testing.T, a interface{}, msg string) { + if a == nil { + t.Errorf("Expected %+v to not to be nil", msg) + } +} + +func expectEquals(t *testing.T, a interface{}, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("Expected %+v to equal %+v", a, b) + } +}