@@ -2,13 +2,15 @@ package providers
22
33import (
44 "context"
5+ "errors"
56 "net/http"
67 "net/http/httptest"
78 "net/url"
8− "testing"
99
1010 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
11− "github.com/stretchr/testify/assert"
11+ . "github.com/onsi/ginkgo"
12+ . "github.com/onsi/ginkgo/extensions/table"
13+ . "github.com/onsi/gomega"
1214)
1315
1416func testGitLabProvider(hostname string) *GitLabProvider {
@@ -39,100 +41,241 @@ func testGitLabBackend() *httptest.Server {
3941 "groups": ["foo", "bar"]
4042 }
4143 `
44+ projectInfo := `
45+ {
46+ "name": "MyProject",
47+ "archived": false,
48+ "path_with_namespace": "my_group/my_project",
49+ "permissions": {
50+ "project_access": null,
51+ "group_access": {
52+ "access_level": 30,
53+ "notification_level": 3
54+ }
55+ }
56+ }
57+ `
58+
59+ personalProjectInfo := `
60+ {
61+ "name": "MyPersonalProject",
62+ "archived": false,
63+ "path_with_namespace": "my_profile/my_personal_project",
64+ "permissions": {
65+ "project_access": {
66+ "access_level": 30,
67+ "notification_level": 3
68+ },
69+ "group_access": null
70+ }
71+ }
72+ `
73+
74+ archivedProjectInfo := `
75+ {
76+ "name": "MyArchivedProject",
77+ "archived": true,
78+ "path_with_namespace": "my_group/my_archived_project",
79+ "permissions": {
80+ "project_access": {
81+ "access_level": 30,
82+ "notification_level": 3
83+ },
84+ "group_access": null
85+ }
86+ }
87+ `
88+
4289 authHeader := "Bearer gitlab_access_token"
4390
4491 return httptest.NewServer(http.HandlerFunc(
4592 func(w http.ResponseWriter, r *http.Request) {
46− if r.URL.Path == "/oauth/userinfo" {
93+ switch r.URL.Path {
94+ case "/oauth/userinfo":
4795 if r.Header["Authorization"][0] == authHeader {
4896 w.WriteHeader(200)
4997 w.Write([]byte(userInfo))
5098 } else {
5199 w.WriteHeader(401)
52100 }
53− } else {
101+ case "/api/v4/projects/my_group/my_project":
102+ if r.Header["Authorization"][0] == authHeader {
103+ w.WriteHeader(200)
104+ w.Write([]byte(projectInfo))
105+ } else {
106+ w.WriteHeader(401)
107+ }
108+ case "/api/v4/projects/my_group/my_archived_project":
109+ if r.Header["Authorization"][0] == authHeader {
110+ w.WriteHeader(200)
111+ w.Write([]byte(archivedProjectInfo))
112+ } else {
113+ w.WriteHeader(401)
114+ }
115+ case "/api/v4/projects/my_profile/my_personal_project":
116+ if r.Header["Authorization"][0] == authHeader {
117+ w.WriteHeader(200)
118+ w.Write([]byte(personalProjectInfo))
119+ } else {
120+ w.WriteHeader(401)
121+ }
122+ case "/api/v4/projects/my_group/my_bad_project":
123+ w.WriteHeader(403)
124+ default:
54125 w.WriteHeader(404)
55126 }
56127 }))
57128}
58129
59−func TestGitLabProviderBadToken(t *testing.T) {
60− b := testGitLabBackend()
61− defer b.Close()
130+var _ = Describe("Gitlab Provider Tests", func() {
131+ var p *GitLabProvider
132+ var b *httptest.Server
62133
63− bURL, _ := url.Parse(b.URL)
64− p := testGitLabProvider(bURL.Host)
134+ BeforeEach(func() {
135+ b = testGitLabBackend()
65136
66− session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"}
67− err := p.EnrichSession(context.Background(), session)
68− assert.Error(t, err)
69−}
137+ bURL, err := url.Parse(b.URL)
138+ Expect(err).To(BeNil())
70139
71−func TestGitLabProviderUnverifiedEmailDenied(t *testing.T) {
72− b := testGitLabBackend()
73− defer b.Close()
140+ p = testGitLabProvider(bURL.Host)
141+ })
74142
75− bURL, _ := url.Parse(b.URL)
76− p := testGitLabProvider(bURL.Host)
143+ AfterEach(func() {
144+ b.Close()
145+ })
77146
78− session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
79− err := p.EnrichSession(context.Background(), session)
80− assert.Error(t, err)
81−}
147+ Context("with bad token", func() {
148+ It("should trigger an error", func() {
149+ p.AllowUnverifiedEmail = false
150+ session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"}
151+ err := p.EnrichSession(context.Background(), session)
152+ Expect(err).To(MatchError(errors.New("failed to retrieve user info: error getting user info: unexpected status \"401\": ")))
153+ })
154+ })
82155
83−func TestGitLabProviderUnverifiedEmailAllowed(t *testing.T) {
84− b := testGitLabBackend()
85− defer b.Close()
156+ Context("when filtering on email", func() {
157+ type emailsTableInput struct {
158+ expectedError error
159+ expectedValue string
160+ allowUnverifiedEmail bool
161+ }
86162
87− bURL, _ := url.Parse(b.URL)
88− p := testGitLabProvider(bURL.Host)
89− p.AllowUnverifiedEmail = true
163+ DescribeTable("should return expected results",
164+ func(in emailsTableInput) {
165+ p.AllowUnverifiedEmail = in.allowUnverifiedEmail
166+ session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
90167
91− session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
92− err := p.EnrichSession(context.Background(), session)
93− assert.NoError(t, err)
94− assert.Equal(t, "foo@bar.com", session.Email)
95−}
168+ err := p.EnrichSession(context.Background(), session)
96169
97−func TestGitLabProviderUsername(t *testing.T) {
98− b := testGitLabBackend()
99− defer b.Close()
170+ if in.expectedError != nil {
171+ Expect(err).To(MatchError(err))
172+ } else {
173+ Expect(err).To(BeNil())
174+ Expect(session.Email).To(Equal(in.expectedValue))
175+ }
176+ },
177+ Entry("unverified email denied", emailsTableInput{
178+ expectedError: errors.New("user email is not verified"),
179+ allowUnverifiedEmail: false,
180+ }),
181+ Entry("unverified email allowed", emailsTableInput{
182+ expectedError: nil,
183+ expectedValue: "foo@bar.com",
184+ allowUnverifiedEmail: true,
185+ }),
186+ )
187+ })
100188
101− bURL, _ := url.Parse(b.URL)
102− p := testGitLabProvider(bURL.Host)
103− p.AllowUnverifiedEmail = true
189+ Context("when filtering on gitlab entities (groups and projects)", func() {
190+ type entitiesTableInput struct {
191+ expectedValue []string
192+ projects []string
193+ groups []string
194+ }
104195
105− session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
106− err := p.EnrichSession(context.Background(), session)
107− assert.NoError(t, err)
108− assert.Equal(t, "FooBar", session.User)
109−}
196+ DescribeTable("should return expected results",
197+ func(in entitiesTableInput) {
198+ p.AllowUnverifiedEmail = true
199+ session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
110200
111−func TestGitLabProviderGroupMembershipValid(t *testing.T) {
112− b := testGitLabBackend()
113− defer b.Close()
201+ err := p.AddProjects(in.projects)
202+ Expect(err).To(BeNil())
203+ p.SetProjectScope()
114204
115− bURL, _ := url.Parse(b.URL)
116− p := testGitLabProvider(bURL.Host)
117− p.AllowUnverifiedEmail = true
118− p.Groups = []string{"foo"}
205+ if len(in.groups) > 0 {
206+ p.Groups = in.groups
207+ }
119208
120− session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
121− err := p.EnrichSession(context.Background(), session)
122− assert.NoError(t, err)
123− assert.Equal(t, "FooBar", session.User)
124−}
209+ err = p.EnrichSession(context.Background(), session)
125210
126−func TestGitLabProviderGroupMembershipMissing(t *testing.T) {
127− b := testGitLabBackend()
128− defer b.Close()
211+ Expect(err).To(BeNil())
212+ Expect(session.Groups).To(Equal(in.expectedValue))
213+ },
214+ Entry("project membership valid on group project", entitiesTableInput{
215+ expectedValue: []string{"project:my_group/my_project"},
216+ projects: []string{"my_group/my_project"},
217+ }),
218+ Entry("project membership invalid on group project, insufficient access level level", entitiesTableInput{
219+ expectedValue: nil,
220+ projects: []string{"my_group/my_project=40"},
221+ }),
222+ Entry("project membership valid on personnal project", entitiesTableInput{
223+ expectedValue: []string{"project:my_profile/my_personal_project"},
224+ projects: []string{"my_profile/my_personal_project"},
225+ }),
226+ Entry("project membership invalid on personnal project, insufficient access level", entitiesTableInput{
227+ expectedValue: nil,
228+ projects: []string{"my_profile/my_personal_project=40"},
229+ }),
230+ Entry("project membership invalid", entitiesTableInput{
231+ expectedValue: nil,
232+ projects: []string{"my_group/my_bad_project"},
233+ }),
234+ Entry("group membership valid", entitiesTableInput{
235+ expectedValue: []string{"group:foo"},
236+ groups: []string{"foo"},
237+ }),
238+ Entry("groups and projects", entitiesTableInput{
239+ expectedValue: []string{"group:foo", "group:baz", "project:my_group/my_project", "project:my_profile/my_personal_project"},
240+ groups: []string{"foo", "baz"},
241+ projects: []string{"my_group/my_project", "my_profile/my_personal_project"},
242+ }),
243+ Entry("archived projects", entitiesTableInput{
244+ expectedValue: nil,
245+ groups: []string{},
246+ projects: []string{"my_group/my_archived_project"},
247+ }),
248+ )
129249
130− bURL, _ := url.Parse(b.URL)
131− p := testGitLabProvider(bURL.Host)
132− p.AllowUnverifiedEmail = true
133− p.Groups = []string{"baz"}
250+ })
134251
135− session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
136− err := p.EnrichSession(context.Background(), session)
137− assert.Error(t, err)
138−}
252+ Context("when generating group list from multiple kind", func() {
253+ type entitiesTableInput struct {
254+ projects []string
255+ groups []string
256+ }
257+
258+ DescribeTable("should prefix entities with group kind", func(in entitiesTableInput) {
259+ p.Groups = in.groups
260+ err := p.AddProjects(in.projects)
261+ Expect(err).To(BeNil())
262+
263+ all := p.PrefixAllowedGroups()
264+
265+ Expect(len(all)).To(Equal(len(in.projects) + len(in.groups)))
266+ },
267+ Entry("simple test case", entitiesTableInput{
268+ projects: []string{"my_group/my_project", "my_group/my_other_project"},
269+ groups: []string{"mygroup", "myothergroup"},
270+ }),
271+ Entry("projects only", entitiesTableInput{
272+ projects: []string{"my_group/my_project", "my_group/my_other_project"},
273+ groups: []string{},
274+ }),
275+ Entry("groups only", entitiesTableInput{
276+ projects: []string{},
277+ groups: []string{"mygroup", "myothergroup"},
278+ }),
279+ )
280+ })
281+})