282 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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"},
 | |
| 			}),
 | |
| 		)
 | |
| 	})
 | |
| })
 |