diff options
author | Jaime Martinez <jmartinez@gitlab.com> | 2021-08-16 09:05:37 +0300 |
---|---|---|
committer | Jaime Martinez <jmartinez@gitlab.com> | 2021-08-16 10:13:33 +0300 |
commit | d2cb598421d42936e0e7c35f314e49f7a9526616 (patch) | |
tree | 1983185eed73386323f420a81dd71d6b1fe752d7 | |
parent | c557db08544f6ee551334f53005c3f45611b7444 (diff) |
tests: more cases for access control enabled
-rw-r--r-- | app.go | 7 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | internal/domain/domain.go | 11 | ||||
-rw-r--r-- | internal/httptransport/metered_round_tripper.go | 5 | ||||
-rw-r--r-- | shared/pages/group.404/private_project/public.zip | bin | 0 -> 352 bytes | |||
-rw-r--r-- | shared/pages/group.404/private_unauthorized/public.zip | bin | 0 -> 352 bytes | |||
-rw-r--r-- | shared/pages/group.auth/group.auth.gitlab-example.com/public.zip | bin | 0 -> 934 bytes | |||
-rw-r--r-- | shared/pages/group.auth/private.project.1/public.zip | bin | 0 -> 550 bytes | |||
-rw-r--r-- | test/acceptance/auth_test.go | 90 | ||||
-rw-r--r-- | test/acceptance/helpers_test.go | 21 | ||||
-rw-r--r-- | test/acceptance/stub_test.go | 68 | ||||
-rw-r--r-- | test/acceptance/testdata/api_responses.go | 23 |
13 files changed, 129 insertions, 99 deletions
@@ -94,6 +94,13 @@ func (a *theApp) redirectToHTTPS(w http.ResponseWriter, r *http.Request, statusC func (a *theApp) getHostAndDomain(r *http.Request) (string, *domain.Domain, error) { host := request.GetHostWithoutPort(r) + + // TODO: @jaime REMOVE THIS CHECK AND OPEN AN ISSUE + if host == a.config.General.Domain || host == "127.0.0.1" { + // skip resolving the domain with the internal API + return host, nil, nil + } + domain, err := a.domain(r.Context(), host) return host, domain, err @@ -9,6 +9,7 @@ require ( github.com/golang/mock v1.3.1 github.com/golangci/golangci-lint v1.27.0 github.com/gorilla/handlers v1.4.2 + github.com/gorilla/mux v1.8.0 github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.2.0 github.com/hashicorp/go-multierror v1.1.1 @@ -225,6 +225,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= diff --git a/internal/domain/domain.go b/internal/domain/domain.go index 94888e34..eb4d8e86 100644 --- a/internal/domain/domain.go +++ b/internal/domain/domain.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "errors" + "fmt" "net/http" "sync" @@ -161,6 +162,7 @@ func (d *Domain) ServeNotFoundHTTP(w http.ResponseWriter, r *http.Request) { // that failed authentication so that we serve the custom namespace error page for // public namespace domains func (d *Domain) serveNamespaceNotFound(w http.ResponseWriter, r *http.Request) { + fmt.Printf("serveNamespaceNotFound-1\n") // clone r and override the path and try to resolve the domain name clonedReq := r.Clone(context.Background()) clonedReq.URL.Path = "/" @@ -177,29 +179,32 @@ func (d *Domain) serveNamespaceNotFound(w http.ResponseWriter, r *http.Request) httperrors.Serve503(w) return } - + fmt.Printf("serveNamespaceNotFound-2\n") + fmt.Printf("namespaceDomain.LookupPath:%+v\n", namespaceDomain.LookupPath) // for namespace domains that have no access control enabled if !namespaceDomain.LookupPath.HasAccessControl { namespaceDomain.ServeNotFoundHTTP(w, r) return } - + fmt.Printf("serveNamespaceNotFound-3\n") httperrors.Serve404(w) } // ServeNotFoundAuthFailed handler to be called when auth failed so the correct custom // 404 page is served. func (d *Domain) ServeNotFoundAuthFailed(w http.ResponseWriter, r *http.Request) { + fmt.Printf("ServeNotFoundAuthFailed-1\n") lookupPath, err := d.GetLookupPath(r) if err != nil { httperrors.Serve404(w) return } + fmt.Printf("ServeNotFoundAuthFailed-2\n") if d.IsNamespaceProject(r) && !lookupPath.HasAccessControl { d.ServeNotFoundHTTP(w, r) return } - + fmt.Printf("ServeNotFoundAuthFailed-3\n") d.serveNamespaceNotFound(w, r) } diff --git a/internal/httptransport/metered_round_tripper.go b/internal/httptransport/metered_round_tripper.go index 8978f824..45dfeb7e 100644 --- a/internal/httptransport/metered_round_tripper.go +++ b/internal/httptransport/metered_round_tripper.go @@ -3,7 +3,6 @@ package httptransport import ( "context" "net/http" - "net/http/httptrace" "strconv" "time" @@ -45,8 +44,8 @@ func NewMeteredRoundTripper(transport http.RoundTripper, name string, tracerVec, func (mrt *meteredRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { start := time.Now() - ctx := httptrace.WithClientTrace(r.Context(), mrt.newTracer(start)) - ctx, cancel := context.WithCancel(ctx) + //ctx := httptrace.WithClientTrace(r.Context(), mrt.newTracer(start)) + ctx, cancel := context.WithCancel(r.Context()) timer := time.AfterFunc(mrt.ttfbTimeout, cancel) defer timer.Stop() diff --git a/shared/pages/group.404/private_project/public.zip b/shared/pages/group.404/private_project/public.zip Binary files differnew file mode 100644 index 00000000..7f158bcf --- /dev/null +++ b/shared/pages/group.404/private_project/public.zip diff --git a/shared/pages/group.404/private_unauthorized/public.zip b/shared/pages/group.404/private_unauthorized/public.zip Binary files differnew file mode 100644 index 00000000..7f158bcf --- /dev/null +++ b/shared/pages/group.404/private_unauthorized/public.zip diff --git a/shared/pages/group.auth/group.auth.gitlab-example.com/public.zip b/shared/pages/group.auth/group.auth.gitlab-example.com/public.zip Binary files differnew file mode 100644 index 00000000..8ff37318 --- /dev/null +++ b/shared/pages/group.auth/group.auth.gitlab-example.com/public.zip diff --git a/shared/pages/group.auth/private.project.1/public.zip b/shared/pages/group.auth/private.project.1/public.zip Binary files differnew file mode 100644 index 00000000..23b7e646 --- /dev/null +++ b/shared/pages/group.auth/private.project.1/public.zip diff --git a/test/acceptance/auth_test.go b/test/acceptance/auth_test.go index 4dceaad0..fd92a582 100644 --- a/test/acceptance/auth_test.go +++ b/test/acceptance/auth_test.go @@ -7,7 +7,6 @@ import ( "net/http" "net/url" "os" - "regexp" "testing" "time" @@ -107,78 +106,7 @@ func TestWhenLoginCallbackWithUnencryptedCode(t *testing.T) { require.Equal(t, http.StatusInternalServerError, authrsp.StatusCode) } -// TODO: NEED TO MOVE THIS to handler in api_responses -func handleAccessControlArtifactRequests(t *testing.T, w http.ResponseWriter, r *http.Request) bool { - authorization := r.Header.Get("Authorization") - - switch { - case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/delayed_200.html`).MatchString(r.URL.Path): - sleepIfAuthorized(t, authorization, w) - return true - case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/404.html`).MatchString(r.URL.Path): - w.WriteHeader(http.StatusNotFound) - return true - case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/500.html`).MatchString(r.URL.Path): - returnIfAuthorized(t, authorization, w, http.StatusInternalServerError) - return true - case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/200.html`).MatchString(r.URL.Path): - returnIfAuthorized(t, authorization, w, http.StatusOK) - return true - case regexp.MustCompile(`/api/v4/projects/group/subgroup/private/jobs/\d+/artifacts/200.html`).MatchString(r.URL.Path): - returnIfAuthorized(t, authorization, w, http.StatusOK) - return true - default: - return false - } -} - -func handleAccessControlRequests(t *testing.T, w http.ResponseWriter, r *http.Request) { - allowedProjects := regexp.MustCompile(`/api/v4/projects/1\d{3}/pages_access`) - deniedProjects := regexp.MustCompile(`/api/v4/projects/2\d{3}/pages_access`) - invalidTokenProjects := regexp.MustCompile(`/api/v4/projects/3\d{3}/pages_access`) - - switch { - case allowedProjects.MatchString(r.URL.Path): - require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) - w.WriteHeader(http.StatusOK) - case deniedProjects.MatchString(r.URL.Path): - require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) - w.WriteHeader(http.StatusUnauthorized) - case invalidTokenProjects.MatchString(r.URL.Path): - require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprint(w, "{\"error\":\"invalid_token\"}") - default: - t.Logf("Unexpected r.URL.RawPath: %q", r.URL.Path) - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusNotFound) - } -} - -func returnIfAuthorized(t *testing.T, authorization string, w http.ResponseWriter, status int) { - if authorization != "" { - require.Equal(t, "Bearer abc", authorization) - w.WriteHeader(status) - } else { - w.WriteHeader(http.StatusNotFound) - } -} - -func sleepIfAuthorized(t *testing.T, authorization string, w http.ResponseWriter) { - if authorization != "" { - require.Equal(t, "Bearer abc", authorization) - time.Sleep(2 * time.Second) - } else { - w.WriteHeader(http.StatusNotFound) - } -} - func TestAccessControlUnderCustomDomainStandalone(t *testing.T) { - skipUnlessEnabled(t, "not-inplace-chroot") - - // - //teardown := RunPagesProcessWithAuth(t, *pagesBinary, supportedListeners(), testServer.URL, "https://public-gitlab-auth.com") - //defer teardown() runPagesWithAuth(t, []ListenSpec{httpListener}) tests := map[string]struct { @@ -254,13 +182,7 @@ func TestAccessControlUnderCustomDomainStandalone(t *testing.T) { } func TestCustomErrorPageWithAuth(t *testing.T) { - skipUnlessEnabled(t, "not-inplace-chroot") - testServer := makeGitLabPagesAccessStub(t) - testServer.Start() - defer testServer.Close() - - teardown := RunPagesProcessWithAuth(t, *pagesBinary, supportedListeners(), testServer.URL, "https://public-gitlab-auth.com") - defer teardown() + runPagesWithAuth(t, []ListenSpec{httpListener}) tests := []struct { name string @@ -290,7 +212,7 @@ func TestCustomErrorPageWithAuth(t *testing.T) { { name: "private_namespace_with_private_project_auth_failed", domain: "group.auth.gitlab-example.com", - // project ID is 2000 + // project ID is 2005 which causes a 401 path: "/private.project.1/unknown", expectedErrorPage: "The page you're looking for could not be found.", }, @@ -722,12 +644,12 @@ func getValidCookieAndState(t *testing.T, domain string) (string, string) { func runPagesWithAuth(t *testing.T, listeners []ListenSpec) { t.Helper() - testServer := makeGitLabPagesAccessStub(t) - testServer.Start() - t.Cleanup(testServer.Close) + //testServer := makeGitLabPagesAccessStub(t) + //testServer.Start() + //t.Cleanup(testServer.Close) configFile := defaultConfigFileWith(t, - "internal-gitlab-server="+testServer.URL, + //"internal-gitlab-server="+testServer.URL, "gitlab-server=https://public-gitlab-auth.com", "auth-redirect-uri=https://projects.gitlab-example.com/auth", ) diff --git a/test/acceptance/helpers_test.go b/test/acceptance/helpers_test.go index 20af4095..376a94b8 100644 --- a/test/acceptance/helpers_test.go +++ b/test/acceptance/helpers_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "github.com/gorilla/mux" "github.com/pires/go-proxyproto" "github.com/stretchr/testify/require" "golang.org/x/net/nettest" @@ -584,7 +585,7 @@ func NewGitlabDomainsSourceStub(t *testing.T, opts *stubOpts) *httptest.Server { currentStatusCount := 0 - mux := http.NewServeMux() + router := mux.NewRouter() statusHandler := func(w http.ResponseWriter, r *http.Request) { if currentStatusCount < opts.statusReadyCount { w.WriteHeader(http.StatusBadGateway) @@ -598,30 +599,38 @@ func NewGitlabDomainsSourceStub(t *testing.T, opts *stubOpts) *httptest.Server { statusHandler = opts.statusHandler } - mux.HandleFunc("/api/v4/internal/pages/status", statusHandler) + router.HandleFunc("/api/v4/internal/pages/status", statusHandler) pagesHandler := defaultAPIHandler(t, opts) if opts.pagesHandler != nil { pagesHandler = opts.pagesHandler } - mux.HandleFunc("/api/v4/internal/pages", pagesHandler) + router.HandleFunc("/api/v4/internal/pages", pagesHandler) authHandler := defaultAuthHandler(t, opts) if opts.authHandler != nil { authHandler = opts.authHandler } - mux.HandleFunc("/oauth/token", authHandler) + router.HandleFunc("/oauth/token", authHandler) userHandler := defaultUserHandler(t, opts) if opts.userHandler != nil { userHandler = opts.userHandler } - mux.HandleFunc("/api/v4/user", userHandler) + router.HandleFunc("/api/v4/user", userHandler) - return httptest.NewServer(mux) + router.HandleFunc("/api/v4/projects/{project_id:[0-9]+}/pages_access", func(w http.ResponseWriter, r *http.Request) { + if handleAccessControlArtifactRequests(t, w, r) { + return + } + + handleAccessControlRequests(t, w, r) + }) + + return httptest.NewServer(router) } func (o *stubOpts) setAPICalled(v bool) { diff --git a/test/acceptance/stub_test.go b/test/acceptance/stub_test.go index a22f798a..6523c672 100644 --- a/test/acceptance/stub_test.go +++ b/test/acceptance/stub_test.go @@ -5,7 +5,9 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "regexp" "testing" + "time" "github.com/stretchr/testify/require" @@ -135,3 +137,69 @@ func CreateGitLabAPISecretKeyFixtureFile(t *testing.T) (filepath string) { return secretfile.Name() } + +// TODO: NEED TO MOVE THIS to handler in api_responses +func handleAccessControlArtifactRequests(t *testing.T, w http.ResponseWriter, r *http.Request) bool { + authorization := r.Header.Get("Authorization") + + switch { + case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/delayed_200.html`).MatchString(r.URL.Path): + sleepIfAuthorized(t, authorization, w) + return true + case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/404.html`).MatchString(r.URL.Path): + w.WriteHeader(http.StatusNotFound) + return true + case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/500.html`).MatchString(r.URL.Path): + returnIfAuthorized(t, authorization, w, http.StatusInternalServerError) + return true + case regexp.MustCompile(`/api/v4/projects/group/private/jobs/\d+/artifacts/200.html`).MatchString(r.URL.Path): + returnIfAuthorized(t, authorization, w, http.StatusOK) + return true + case regexp.MustCompile(`/api/v4/projects/group/subgroup/private/jobs/\d+/artifacts/200.html`).MatchString(r.URL.Path): + returnIfAuthorized(t, authorization, w, http.StatusOK) + return true + default: + return false + } +} + +func handleAccessControlRequests(t *testing.T, w http.ResponseWriter, r *http.Request) { + allowedProjects := regexp.MustCompile(`/api/v4/projects/1\d{3}/pages_access`) + deniedProjects := regexp.MustCompile(`/api/v4/projects/2\d{3}/pages_access`) + invalidTokenProjects := regexp.MustCompile(`/api/v4/projects/3\d{3}/pages_access`) + + switch { + case allowedProjects.MatchString(r.URL.Path): + require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) + w.WriteHeader(http.StatusOK) + case deniedProjects.MatchString(r.URL.Path): + require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) + w.WriteHeader(http.StatusUnauthorized) + case invalidTokenProjects.MatchString(r.URL.Path): + require.Equal(t, "Bearer abc", r.Header.Get("Authorization")) + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprint(w, "{\"error\":\"invalid_token\"}") + default: + t.Logf("Unexpected r.URL.RawPath: %q", r.URL.Path) + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusNotFound) + } +} + +func returnIfAuthorized(t *testing.T, authorization string, w http.ResponseWriter, status int) { + if authorization != "" { + require.Equal(t, "Bearer abc", authorization) + w.WriteHeader(status) + } else { + w.WriteHeader(http.StatusNotFound) + } +} + +func sleepIfAuthorized(t *testing.T, authorization string, w http.ResponseWriter) { + if authorization != "" { + require.Equal(t, "Bearer abc", authorization) + time.Sleep(2 * time.Second) + } else { + w.WriteHeader(http.StatusNotFound) + } +} diff --git a/test/acceptance/testdata/api_responses.go b/test/acceptance/testdata/api_responses.go index 80e761d7..4bc0b24e 100644 --- a/test/acceptance/testdata/api_responses.go +++ b/test/acceptance/testdata/api_responses.go @@ -27,7 +27,16 @@ var DomainResponses = map[string]responseFn{ "zip-not-allowed-path.gitlab.io": customDomain(projectConfig{pathOnDisk: "../../../../"}), "group.gitlab-example.com": generateVirtualDomainFromDir("group", "group.gitlab-example.com", nil), "CapitalGroup.gitlab-example.com": generateVirtualDomainFromDir("CapitalGroup", "CapitalGroup.gitlab-example.com", nil), - "group.404.gitlab-example.com": generateVirtualDomainFromDir("group.404", "group.404.gitlab-example.com", nil), + "group.404.gitlab-example.com": generateVirtualDomainFromDir("group.404", "group.404.gitlab-example.com", map[string]projectConfig{ + "/private_project": { + projectID: 1300, + accessControl: true, + }, + "/private_unauthorized": { + projectID: 2000, + accessControl: true, + }, + }), "group.https-only.gitlab-example.com": generateVirtualDomainFromDir("group.https-only", "group.https-only.gitlab-example.com", map[string]projectConfig{ "/project1": { projectID: 1000, @@ -67,13 +76,21 @@ var DomainResponses = map[string]responseFn{ pathOnDisk: "group.https-only/project5", }), "group.auth.gitlab-example.com": generateVirtualDomainFromDir("group.auth", "group.auth.gitlab-example.com", map[string]projectConfig{ - "/private.project": { + "/": { projectID: 1005, accessControl: true, }, + "/private.project": { + projectID: 1006, + accessControl: true, + }, + "/private.project.1": { + projectID: 2006, + accessControl: true, + }, }), "private.domain.com": customDomain(projectConfig{ - projectID: 1006, + projectID: 1007, accessControl: true, pathOnDisk: "group.auth/private.project", }), |