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"
13 . "github.com/onsi/ginkgo/extensions/table"
14 . "github.com/onsi/gomega"
17func testGitLabProvider(hostname, scope string, opts options.GitLabOptions) (*GitLabProvider, error) {
18 p, err := NewGitLabProvider(
22 RedeemURL: &url.URL{},
23 ProfileURL: &url.URL{},
24 ValidateURL: &url.URL{},
31 updateURL(p.Data().LoginURL, hostname)
32 updateURL(p.Data().RedeemURL, hostname)
33 updateURL(p.Data().ProfileURL, hostname)
34 updateURL(p.Data().ValidateURL, hostname)
40func testGitLabBackend() *httptest.Server {
44 "email": "foo@bar.com",
45 "email_verified": false,
46 "groups": ["foo", "bar"]
54 "path_with_namespace": "my_group/my_project",
56 "project_access": null,
59 "notification_level": 3
65 noAccessProjectInfo := `
67 "name": "NoAccessProject",
69 "path_with_namespace": "no_access_group/no_access_project",
71 "project_access": null,
77 personalProjectInfo := `
79 "name": "MyPersonalProject",
81 "path_with_namespace": "my_profile/my_personal_project",
85 "notification_level": 3
92 archivedProjectInfo := `
94 "name": "MyArchivedProject",
96 "path_with_namespace": "my_group/my_archived_project",
100 "notification_level": 3
107 authHeader := "Bearer gitlab_access_token"
109 return httptest.NewServer(http.HandlerFunc(
110 func(w http.ResponseWriter, r *http.Request) {
112 case "/oauth/userinfo":
113 if r.Header["Authorization"][0] == authHeader {
115 w.Write([]byte(userInfo))
119 case "/api/v4/projects/my_group/my_project":
120 if r.Header["Authorization"][0] == authHeader {
122 w.Write([]byte(projectInfo))
126 case "/api/v4/projects/no_access_group/no_access_project":
127 if r.Header["Authorization"][0] == authHeader {
129 w.Write([]byte(noAccessProjectInfo))
133 case "/api/v4/projects/my_group/my_archived_project":
134 if r.Header["Authorization"][0] == authHeader {
136 w.Write([]byte(archivedProjectInfo))
140 case "/api/v4/projects/my_profile/my_personal_project":
141 if r.Header["Authorization"][0] == authHeader {
143 w.Write([]byte(personalProjectInfo))
147 case "/api/v4/projects/my_group/my_bad_project":
155var _ = Describe("Gitlab Provider Tests", func() {
156 var p *GitLabProvider
157 var b *httptest.Server
160 b = testGitLabBackend()
162 bURL, err := url.Parse(b.URL)
163 Expect(err).To(BeNil())
165 p, err = testGitLabProvider(bURL.Host, "", options.GitLabOptions{})
166 Expect(err).ToNot(HaveOccurred())
173 Context("with bad token", func() {
174 It("should trigger an error", func() {
175 p.AllowUnverifiedEmail = false
176 session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"}
177 err := p.EnrichSession(context.Background(), session)
178 Expect(err).To(MatchError(errors.New("failed to retrieve user info: error getting user info: unexpected status \"401\": ")))
182 Context("when filtering on email", func() {
183 type emailsTableInput struct {
186 allowUnverifiedEmail bool
189 DescribeTable("should return expected results",
190 func(in emailsTableInput) {
191 p.AllowUnverifiedEmail = in.allowUnverifiedEmail
192 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
194 err := p.EnrichSession(context.Background(), session)
196 if in.expectedError != nil {
197 Expect(err).To(MatchError(in.expectedError))
199 Expect(err).To(BeNil())
200 Expect(session.Email).To(Equal(in.expectedValue))
203 Entry("unverified email denied", emailsTableInput{
204 expectedError: errors.New("user email is not verified"),
205 allowUnverifiedEmail: false,
207 Entry("unverified email allowed", emailsTableInput{
209 expectedValue: "foo@bar.com",
210 allowUnverifiedEmail: true,
215 Context("when filtering on gitlab entities (groups and projects)", func() {
216 type entitiesTableInput struct {
217 allowedProjects []string
218 allowedGroups []string
222 expectedGroups []string
226 DescribeTable("should return expected results",
227 func(in entitiesTableInput) {
228 bURL, err := url.Parse(b.URL)
229 Expect(err).To(BeNil())
231 p, err := testGitLabProvider(bURL.Host, in.scope, options.GitLabOptions{
232 Group: in.allowedGroups,
233 Projects: in.allowedProjects,
235 if in.expectedError == nil {
236 Expect(err).To(BeNil())
238 Expect(err).To(MatchError(in.expectedError))
242 p.AllowUnverifiedEmail = true
243 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
245 Expect(p.Scope).To(Equal(in.expectedScope))
247 err = p.EnrichSession(context.Background(), session)
248 Expect(err).To(BeNil())
249 Expect(session.Groups).To(Equal(in.expectedGroups))
251 authorized, err := p.Authorize(context.Background(), session)
252 Expect(err).To(BeNil())
253 Expect(authorized).To(Equal(in.expectedAuthz))
255 Entry("project membership valid on group project", entitiesTableInput{
256 allowedProjects: []string{"my_group/my_project"},
258 expectedGroups: []string{"foo", "bar", "project:my_group/my_project"},
259 expectedScope: "openid email read_api",
261 Entry("project membership invalid on group project, insufficient access level level", entitiesTableInput{
262 allowedProjects: []string{"my_group/my_project=40"},
263 expectedAuthz: false,
264 expectedGroups: []string{"foo", "bar"},
265 expectedScope: "openid email read_api",
267 Entry("project membership invalid on group project, no access at all", entitiesTableInput{
268 allowedProjects: []string{"no_access_group/no_access_project=30"},
269 expectedAuthz: false,
270 expectedGroups: []string{"foo", "bar"},
271 expectedScope: "openid email read_api",
273 Entry("project membership valid on personnal project", entitiesTableInput{
274 allowedProjects: []string{"my_profile/my_personal_project"},
275 scope: "openid email read_api profile",
277 expectedGroups: []string{"foo", "bar", "project:my_profile/my_personal_project"},
278 expectedScope: "openid email read_api profile",
280 Entry("project membership invalid on personnal project, insufficient access level", entitiesTableInput{
281 allowedProjects: []string{"my_profile/my_personal_project=40"},
282 expectedAuthz: false,
283 expectedGroups: []string{"foo", "bar"},
284 expectedScope: "openid email read_api",
286 Entry("project membership invalid", entitiesTableInput{
287 allowedProjects: []string{"my_group/my_bad_project"},
288 expectedAuthz: false,
289 expectedGroups: []string{"foo", "bar"},
290 expectedScope: "openid email read_api",
292 Entry("group membership valid", entitiesTableInput{
293 allowedGroups: []string{"foo"},
294 expectedGroups: []string{"foo", "bar"},
296 expectedScope: "openid email",
298 Entry("groups and projects", entitiesTableInput{
299 allowedGroups: []string{"foo", "baz"},
300 allowedProjects: []string{"my_group/my_project", "my_profile/my_personal_project"},
302 expectedGroups: []string{"foo", "bar", "project:my_group/my_project", "project:my_profile/my_personal_project"},
303 expectedScope: "openid email read_api",
305 Entry("archived projects", entitiesTableInput{
306 allowedProjects: []string{"my_group/my_archived_project"},
307 expectedAuthz: false,
308 expectedGroups: []string{"foo", "bar"},
309 expectedScope: "openid email read_api",
311 Entry("invalid project format", entitiesTableInput{
312 allowedProjects: []string{"my_group/my_invalid_project=123"},
313 expectedError: errors.New("could not configure allowed projects: invalid gitlab project access level specified (my_group/my_invalid_project)"),
314 expectedScope: "openid email read_api",
319 Context("when refreshing", func() {
320 It("keeps the existing nickname after refreshing", func() {
321 session := &sessions.SessionState{
324 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
328 refreshed, err := p.RefreshSession(context.Background(), session)
329 Expect(refreshed).To(BeTrue())
330 Expect(err).ToNot(HaveOccurred())
331 Expect(session.User).To(Equal("nickname"))
333 It("keeps existing projects after refreshing groups", func() {
334 session := &sessions.SessionState{}
335 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
337 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
338 s.Groups = []string{"baz"}
342 refreshed, err := p.RefreshSession(context.Background(), session)
343 Expect(refreshed).To(BeTrue())
344 Expect(err).ToNot(HaveOccurred())
345 Expect(len(session.Groups)).To(Equal(3))
346 Expect(session.Groups).
347 To(ContainElements([]string{"baz", "project:thing", "project:sample"}))
349 It("leaves existing groups when not refreshed", func() {
350 session := &sessions.SessionState{}
351 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
353 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
357 refreshed, err := p.RefreshSession(context.Background(), session)
358 Expect(refreshed).To(BeFalse())
359 Expect(err).ToNot(HaveOccurred())
360 Expect(len(session.Groups)).To(Equal(4))
361 Expect(session.Groups).
362 To(ContainElements([]string{"foo", "bar", "project:thing", "project:sample"}))
364 It("leaves existing groups when OIDC refresh errors", func() {
365 session := &sessions.SessionState{}
366 session.Groups = []string{"foo", "bar", "project:thing", "project:sample"}
368 p.oidcRefreshFunc = func(_ context.Context, s *sessions.SessionState) (bool, error) {
369 return false, errors.New("failure")
372 refreshed, err := p.RefreshSession(context.Background(), session)
373 Expect(refreshed).To(BeFalse())
374 Expect(err).To(HaveOccurred())
375 Expect(len(session.Groups)).To(Equal(4))
376 Expect(session.Groups).
377 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