package providers import ( "context" "errors" "net/http" "net/http/httptest" "net/url" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" ) func testGitLabProvider(hostname string) *GitLabProvider { p := NewGitLabProvider( &ProviderData{ ProviderName: "", LoginURL: &url.URL{}, RedeemURL: &url.URL{}, ProfileURL: &url.URL{}, ValidateURL: &url.URL{}, Scope: ""}) 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 testGitLabBackend() *httptest.Server { userInfo := ` { "nickname": "FooBar", "email": "foo@bar.com", "email_verified": false, "groups": ["foo", "bar"] } ` projectInfo := ` { "name": "MyProject", "archived": false, "path_with_namespace": "my_group/my_project", "permissions": { "project_access": null, "group_access": { "access_level": 30, "notification_level": 3 } } } ` personalProjectInfo := ` { "name": "MyPersonalProject", "archived": false, "path_with_namespace": "my_profile/my_personal_project", "permissions": { "project_access": { "access_level": 30, "notification_level": 3 }, "group_access": null } } ` archivedProjectInfo := ` { "name": "MyArchivedProject", "archived": true, "path_with_namespace": "my_group/my_archived_project", "permissions": { "project_access": { "access_level": 30, "notification_level": 3 }, "group_access": null } } ` authHeader := "Bearer gitlab_access_token" return httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/oauth/userinfo": if r.Header["Authorization"][0] == authHeader { w.WriteHeader(200) w.Write([]byte(userInfo)) } else { w.WriteHeader(401) } case "/api/v4/projects/my_group/my_project": if r.Header["Authorization"][0] == authHeader { w.WriteHeader(200) w.Write([]byte(projectInfo)) } else { w.WriteHeader(401) } case "/api/v4/projects/my_group/my_archived_project": if r.Header["Authorization"][0] == authHeader { w.WriteHeader(200) w.Write([]byte(archivedProjectInfo)) } else { w.WriteHeader(401) } case "/api/v4/projects/my_profile/my_personal_project": if r.Header["Authorization"][0] == authHeader { w.WriteHeader(200) w.Write([]byte(personalProjectInfo)) } else { w.WriteHeader(401) } case "/api/v4/projects/my_group/my_bad_project": w.WriteHeader(403) default: w.WriteHeader(404) } })) } var _ = Describe("Gitlab Provider Tests", func() { var p *GitLabProvider var b *httptest.Server BeforeEach(func() { b = testGitLabBackend() bURL, err := url.Parse(b.URL) Expect(err).To(BeNil()) p = testGitLabProvider(bURL.Host) }) AfterEach(func() { b.Close() }) Context("with bad token", func() { It("should trigger an error", func() { p.AllowUnverifiedEmail = false session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"} err := p.EnrichSession(context.Background(), session) Expect(err).To(MatchError(errors.New("failed to retrieve user info: error getting user info: unexpected status \"401\": "))) }) }) Context("when filtering on email", func() { type emailsTableInput struct { expectedError error expectedValue string allowUnverifiedEmail bool } DescribeTable("should return expected results", func(in emailsTableInput) { p.AllowUnverifiedEmail = in.allowUnverifiedEmail session := &sessions.SessionState{AccessToken: "gitlab_access_token"} err := p.EnrichSession(context.Background(), session) if in.expectedError != nil { Expect(err).To(MatchError(err)) } else { Expect(err).To(BeNil()) Expect(session.Email).To(Equal(in.expectedValue)) } }, Entry("unverified email denied", emailsTableInput{ expectedError: errors.New("user email is not verified"), allowUnverifiedEmail: false, }), Entry("unverified email allowed", emailsTableInput{ expectedError: nil, expectedValue: "foo@bar.com", allowUnverifiedEmail: true, }), ) }) Context("when filtering on gitlab entities (groups and projects)", func() { type entitiesTableInput struct { expectedValue []string projects []string groups []string } DescribeTable("should return expected results", func(in entitiesTableInput) { p.AllowUnverifiedEmail = true session := &sessions.SessionState{AccessToken: "gitlab_access_token"} err := p.AddProjects(in.projects) Expect(err).To(BeNil()) p.SetProjectScope() if len(in.groups) > 0 { p.Groups = in.groups } err = p.EnrichSession(context.Background(), session) Expect(err).To(BeNil()) Expect(session.Groups).To(Equal(in.expectedValue)) }, Entry("project membership valid on group project", entitiesTableInput{ expectedValue: []string{"project:my_group/my_project"}, projects: []string{"my_group/my_project"}, }), Entry("project membership invalid on group project, insufficient access level level", entitiesTableInput{ expectedValue: nil, projects: []string{"my_group/my_project=40"}, }), Entry("project membership valid on personnal project", entitiesTableInput{ expectedValue: []string{"project:my_profile/my_personal_project"}, projects: []string{"my_profile/my_personal_project"}, }), Entry("project membership invalid on personnal project, insufficient access level", entitiesTableInput{ expectedValue: nil, projects: []string{"my_profile/my_personal_project=40"}, }), Entry("project membership invalid", entitiesTableInput{ expectedValue: nil, projects: []string{"my_group/my_bad_project"}, }), Entry("group membership valid", entitiesTableInput{ expectedValue: []string{"group:foo"}, groups: []string{"foo"}, }), Entry("groups and projects", entitiesTableInput{ expectedValue: []string{"group:foo", "group:baz", "project:my_group/my_project", "project:my_profile/my_personal_project"}, groups: []string{"foo", "baz"}, projects: []string{"my_group/my_project", "my_profile/my_personal_project"}, }), Entry("archived projects", entitiesTableInput{ expectedValue: nil, groups: []string{}, projects: []string{"my_group/my_archived_project"}, }), ) }) Context("when generating group list from multiple kind", func() { type entitiesTableInput struct { projects []string groups []string } DescribeTable("should prefix entities with group kind", func(in entitiesTableInput) { p.Groups = in.groups err := p.AddProjects(in.projects) Expect(err).To(BeNil()) all := p.PrefixAllowedGroups() Expect(len(all)).To(Equal(len(in.projects) + len(in.groups))) }, Entry("simple test case", entitiesTableInput{ projects: []string{"my_group/my_project", "my_group/my_other_project"}, groups: []string{"mygroup", "myothergroup"}, }), Entry("projects only", entitiesTableInput{ projects: []string{"my_group/my_project", "my_group/my_other_project"}, groups: []string{}, }), Entry("groups only", entitiesTableInput{ projects: []string{}, groups: []string{"mygroup", "myothergroup"}, }), ) }) })