10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
12 . "github.com/onsi/ginkgo/v2"
13 . "github.com/onsi/gomega"
16func testGitLabProvider(hostname, scope string, opts options.Provider) (*GitLabProvider, error) {
17 p, err := NewGitLabProvider(
21 RedeemURL: &url.URL{},
22 ProfileURL: &url.URL{},
23 ValidateURL: &url.URL{},
30 updateURL(p.Data().LoginURL, hostname)
31 updateURL(p.Data().RedeemURL, hostname)
32 updateURL(p.Data().ProfileURL, hostname)
33 updateURL(p.Data().ValidateURL, hostname)
39func testGitLabBackend() *httptest.Server {
43 "email": "foo@bar.com",
44 "email_verified": false,
45 "groups": ["foo", "bar"]
53 "path_with_namespace": "my_group/my_project",
55 "project_access": null,
58 "notification_level": 3
64 noAccessProjectInfo := `
66 "name": "NoAccessProject",
68 "path_with_namespace": "no_access_group/no_access_project",
70 "project_access": null,
76 personalProjectInfo := `
78 "name": "MyPersonalProject",
80 "path_with_namespace": "my_profile/my_personal_project",
84 "notification_level": 3
91 archivedProjectInfo := `
93 "name": "MyArchivedProject",
95 "path_with_namespace": "my_group/my_archived_project",
99 "notification_level": 3
106 authHeader := "Bearer gitlab_access_token"
108 return httptest.NewServer(http.HandlerFunc(
109 func(w http.ResponseWriter, r *http.Request) {
111 case "/oauth/userinfo":
112 if r.Header["Authorization"][0] == authHeader {
114 w.Write([]byte(userInfo))
118 case "/api/v4/projects/my_group/my_project":
119 if r.Header["Authorization"][0] == authHeader {
121 w.Write([]byte(projectInfo))
125 case "/api/v4/projects/no_access_group/no_access_project":
126 if r.Header["Authorization"][0] == authHeader {
128 w.Write([]byte(noAccessProjectInfo))
132 case "/api/v4/projects/my_group/my_archived_project":
133 if r.Header["Authorization"][0] == authHeader {
135 w.Write([]byte(archivedProjectInfo))
139 case "/api/v4/projects/my_profile/my_personal_project":
140 if r.Header["Authorization"][0] == authHeader {
142 w.Write([]byte(personalProjectInfo))
146 case "/api/v4/projects/my_group/my_bad_project":
154var _ = Describe("Gitlab Provider Tests", func() {
155 var p *GitLabProvider
156 var b *httptest.Server
159 b = testGitLabBackend()
161 bURL, err := url.Parse(b.URL)
162 Expect(err).To(BeNil())
164 p, err = testGitLabProvider(bURL.Host, "", options.Provider{})
165 Expect(err).ToNot(HaveOccurred())
172 Context("New Provider Init", func() {
173 It("creates new keycloak oidc provider with expected defaults", func() {
174 providerData := p.Data()
175 Expect(providerData.ProviderName).To(Equal(gitlabProviderName))
176 Expect(providerData.Scope).To(Equal(gitlabDefaultScope))
177 Expect(providerData.ProviderName).NotTo(Equal(oidcDefaultScope))
181 Context("with bad token", func() {
182 It("should trigger an error", func() {
183 p.AllowUnverifiedEmail = false
184 session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"}
185 err := p.EnrichSession(context.Background(), session)
186 Expect(err).To(MatchError(errors.New("failed to retrieve user info: error getting user info: unexpected status \"401\": ")))
190 Context("when filtering on email", func() {
191 type emailsTableInput struct {
194 allowUnverifiedEmail bool
197 DescribeTable("should return expected results",
198 func(in emailsTableInput) {
199 p.AllowUnverifiedEmail = in.allowUnverifiedEmail
200 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
202 err := p.EnrichSession(context.Background(), session)
204 if in.expectedError != nil {
205 Expect(err).To(MatchError(in.expectedError))
207 Expect(err).To(BeNil())
208 Expect(session.Email).To(Equal(in.expectedValue))
211 Entry("unverified email denied", emailsTableInput{
212 expectedError: errors.New("user email is not verified"),
213 allowUnverifiedEmail: false,
215 Entry("unverified email allowed", emailsTableInput{
217 expectedValue: "foo@bar.com",
218 allowUnverifiedEmail: true,
223 Context("when filtering on gitlab entities (groups and projects)", func() {
224 type entitiesTableInput struct {
225 allowedProjects []string
226 allowedGroups []string
230 expectedGroups []string
234 DescribeTable("should return expected results",
235 func(in entitiesTableInput) {
236 bURL, err := url.Parse(b.URL)
237 Expect(err).To(BeNil())
239 p, err := testGitLabProvider(bURL.Host, in.scope, options.Provider{
240 GitLabConfig: options.GitLabOptions{
241 Group: in.allowedGroups,
242 Projects: in.allowedProjects,
245 if in.expectedError == nil {
246 Expect(err).To(BeNil())
248 Expect(err).To(MatchError(in.expectedError))
252 p.AllowUnverifiedEmail = true
253 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
255 Expect(p.Scope).To(Equal(in.expectedScope))
257 err = p.EnrichSession(context.Background(), session)
258 Expect(err).To(BeNil())
259 Expect(session.Groups).To(Equal(in.expectedGroups))
261 authorized, err := p.Authorize(context.Background(), session)
262 Expect(err).To(BeNil())
263 Expect(authorized).To(Equal(in.expectedAuthz))
265 Entry("project membership valid on group project", entitiesTableInput{
266 allowedProjects: []string{"my_group/my_project"},
268 expectedGroups: []string{"foo", "bar", "project:my_group/my_project"},
269 expectedScope: "openid email read_api",
271 Entry("project membership invalid on group project, insufficient access level level", entitiesTableInput{
272 allowedProjects: []string{"my_group/my_project=40"},
273 expectedAuthz: false,
274 expectedGroups: []string{"foo", "bar"},
275 expectedScope: "openid email read_api",
277 Entry("project membership invalid on group project, no access at all", entitiesTableInput{
278 allowedProjects: []string{"no_access_group/no_access_project=30"},
279 expectedAuthz: false,
280 expectedGroups: []string{"foo", "bar"},
281 expectedScope: "openid email read_api",
283 Entry("project membership valid on personnal project", entitiesTableInput{
284 allowedProjects: []string{"my_profile/my_personal_project"},
285 scope: "openid email read_api profile",
287 expectedGroups: []string{"foo", "bar", "project:my_profile/my_personal_project"},
288 expectedScope: "openid email read_api profile",
290 Entry("project membership invalid on personnal project, insufficient access level", entitiesTableInput{
291 allowedProjects: []string{"my_profile/my_personal_project=40"},
292 expectedAuthz: false,
293 expectedGroups: []string{"foo", "bar"},
294 expectedScope: "openid email read_api",
296 Entry("project membership invalid", entitiesTableInput{
297 allowedProjects: []string{"my_group/my_bad_project"},
298 expectedAuthz: false,
299 expectedGroups: []string{"foo", "bar"},
300 expectedScope: "openid email read_api",
302 Entry("group membership valid", entitiesTableInput{
303 allowedGroups: []string{"foo"},
304 expectedGroups: []string{"foo", "bar"},
306 expectedScope: "openid email",
308 Entry("groups and projects", entitiesTableInput{
309 allowedGroups: []string{"foo", "baz"},
310 allowedProjects: []string{"my_group/my_project", "my_profile/my_personal_project"},
312 expectedGroups: []string{"foo", "bar", "project:my_group/my_project", "project:my_profile/my_personal_project"},
313 expectedScope: "openid email read_api",
315 Entry("archived projects", entitiesTableInput{
316 allowedProjects: []string{"my_group/my_archived_project"},
317 expectedAuthz: false,
318 expectedGroups: []string{"foo", "bar"},
319 expectedScope: "openid email read_api",
321 Entry("invalid project format", entitiesTableInput{
322 allowedProjects: []string{"my_group/my_invalid_project=123"},
323 expectedError: errors.New("could not configure allowed projects: invalid gitlab project access level specified (my_group/my_invalid_project)"),
324 expectedScope: "openid email read_api",
329 Context("when refreshing", func() {
330 It("keeps the existing nickname after refreshing", func() {
331 session := &sessions.SessionState{
334 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
338 refreshed, err := p.RefreshSession(context.Background(), session)
339 Expect(refreshed).To(BeTrue())
340 Expect(err).ToNot(HaveOccurred())
341 Expect(session.User).To(Equal("nickname"))
343 It("keeps existing projects after refreshing groups", func() {
344 session := &sessions.SessionState{}
345 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
347 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
348 s.Groups = []string{"baz"}
352 refreshed, err := p.RefreshSession(context.Background(), session)
353 Expect(refreshed).To(BeTrue())
354 Expect(err).ToNot(HaveOccurred())
355 Expect(len(session.Groups)).To(Equal(3))
356 Expect(session.Groups).
357 To(ContainElements([]string{"baz", "project:thing", "project:sample"}))
359 It("leaves existing groups when not refreshed", func() {
360 session := &sessions.SessionState{}
361 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
363 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
367 refreshed, err := p.RefreshSession(context.Background(), session)
368 Expect(refreshed).To(BeFalse())
369 Expect(err).ToNot(HaveOccurred())
370 Expect(len(session.Groups)).To(Equal(4))
371 Expect(session.Groups).
372 To(ContainElements([]string{"foo", "bar", "project:thing", "project:sample"}))
374 It("leaves existing groups when OIDC refresh errors", func() {
375 session := &sessions.SessionState{}
376 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
378 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
379 return false, errors.New("failure")
382 refreshed, err := p.RefreshSession(context.Background(), session)
383 Expect(refreshed).To(BeFalse())
384 Expect(err).To(HaveOccurred())
385 Expect(len(session.Groups)).To(Equal(4))
386 Expect(session.Groups).
387 To(ContainElements([]string{"foo", "bar", "project:thing", "project:sample"}))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list