X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/2b699dec710d1f0719ec0471bc711a467ac34a95..d22400822b3489c621e5dbd902749b1a547ca579:/services/keep-web/handler_test.go diff --git a/services/keep-web/handler_test.go b/services/keep-web/handler_test.go index d04c5c2d10..df0346ba31 100644 --- a/services/keep-web/handler_test.go +++ b/services/keep-web/handler_test.go @@ -18,6 +18,66 @@ var _ = check.Suite(&UnitSuite{}) type UnitSuite struct{} +func (s *UnitSuite) TestCORSPreflight(c *check.C) { + h := handler{Config: DefaultConfig()} + u, _ := url.Parse("http://keep-web.example/c=" + arvadostest.FooCollection + "/foo") + req := &http.Request{ + Method: "OPTIONS", + Host: u.Host, + URL: u, + RequestURI: u.RequestURI(), + Header: http.Header{ + "Origin": {"https://workbench.example"}, + "Access-Control-Request-Method": {"POST"}, + }, + } + + // Check preflight for an allowed request + resp := httptest.NewRecorder() + h.ServeHTTP(resp, req) + c.Check(resp.Code, check.Equals, http.StatusOK) + c.Check(resp.Body.String(), check.Equals, "") + c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*") + c.Check(resp.Header().Get("Access-Control-Allow-Methods"), check.Equals, "GET, POST") + c.Check(resp.Header().Get("Access-Control-Allow-Headers"), check.Equals, "Range") + + // Check preflight for a disallowed request + resp = httptest.NewRecorder() + req.Header.Set("Access-Control-Request-Method", "DELETE") + h.ServeHTTP(resp, req) + c.Check(resp.Body.String(), check.Equals, "") + c.Check(resp.Code, check.Equals, http.StatusMethodNotAllowed) +} + +func (s *UnitSuite) TestInvalidUUID(c *check.C) { + bogusID := strings.Replace(arvadostest.FooPdh, "+", "-", 1) + "-" + token := arvadostest.ActiveToken + for _, trial := range []string{ + "http://keep-web/c=" + bogusID + "/foo", + "http://keep-web/c=" + bogusID + "/t=" + token + "/foo", + "http://keep-web/collections/download/" + bogusID + "/" + token + "/foo", + "http://keep-web/collections/" + bogusID + "/foo", + "http://" + bogusID + ".keep-web/" + bogusID + "/foo", + "http://" + bogusID + ".keep-web/t=" + token + "/" + bogusID + "/foo", + } { + c.Log(trial) + u, err := url.Parse(trial) + c.Assert(err, check.IsNil) + req := &http.Request{ + Method: "GET", + Host: u.Host, + URL: u, + RequestURI: u.RequestURI(), + } + resp := httptest.NewRecorder() + cfg := DefaultConfig() + cfg.AnonymousTokens = []string{arvadostest.AnonymousToken} + h := handler{Config: cfg} + h.ServeHTTP(resp, req) + c.Check(resp.Code, check.Equals, http.StatusNotFound) + } +} + func mustParseURL(s string) *url.URL { r, err := url.Parse(s) if err != nil { @@ -38,7 +98,7 @@ func (s *IntegrationSuite) TestVhost404(c *check.C) { URL: u, RequestURI: u.RequestURI(), } - (&handler{}).ServeHTTP(resp, req) + s.testServer.Handler.ServeHTTP(resp, req) c.Check(resp.Code, check.Equals, http.StatusNotFound) c.Check(resp.Body.String(), check.Equals, "") } @@ -51,7 +111,7 @@ func (s *IntegrationSuite) TestVhost404(c *check.C) { type authorizer func(*http.Request, string) int func (s *IntegrationSuite) TestVhostViaAuthzHeader(c *check.C) { - doVhostRequests(c, authzViaAuthzHeader) + s.doVhostRequests(c, authzViaAuthzHeader) } func authzViaAuthzHeader(r *http.Request, tok string) int { r.Header.Add("Authorization", "OAuth2 "+tok) @@ -59,7 +119,7 @@ func authzViaAuthzHeader(r *http.Request, tok string) int { } func (s *IntegrationSuite) TestVhostViaCookieValue(c *check.C) { - doVhostRequests(c, authzViaCookieValue) + s.doVhostRequests(c, authzViaCookieValue) } func authzViaCookieValue(r *http.Request, tok string) int { r.AddCookie(&http.Cookie{ @@ -70,7 +130,7 @@ func authzViaCookieValue(r *http.Request, tok string) int { } func (s *IntegrationSuite) TestVhostViaPath(c *check.C) { - doVhostRequests(c, authzViaPath) + s.doVhostRequests(c, authzViaPath) } func authzViaPath(r *http.Request, tok string) int { r.URL.Path = "/t=" + tok + r.URL.Path @@ -78,7 +138,7 @@ func authzViaPath(r *http.Request, tok string) int { } func (s *IntegrationSuite) TestVhostViaQueryString(c *check.C) { - doVhostRequests(c, authzViaQueryString) + s.doVhostRequests(c, authzViaQueryString) } func authzViaQueryString(r *http.Request, tok string) int { r.URL.RawQuery = "api_token=" + tok @@ -86,7 +146,7 @@ func authzViaQueryString(r *http.Request, tok string) int { } func (s *IntegrationSuite) TestVhostViaPOST(c *check.C) { - doVhostRequests(c, authzViaPOST) + s.doVhostRequests(c, authzViaPOST) } func authzViaPOST(r *http.Request, tok string) int { r.Method = "POST" @@ -97,7 +157,7 @@ func authzViaPOST(r *http.Request, tok string) int { } func (s *IntegrationSuite) TestVhostViaXHRPOST(c *check.C) { - doVhostRequests(c, authzViaPOST) + s.doVhostRequests(c, authzViaPOST) } func authzViaXHRPOST(r *http.Request, tok string) int { r.Method = "POST" @@ -113,7 +173,7 @@ func authzViaXHRPOST(r *http.Request, tok string) int { // Try some combinations of {url, token} using the given authorization // mechanism, and verify the result is correct. -func doVhostRequests(c *check.C, authz authorizer) { +func (s *IntegrationSuite) doVhostRequests(c *check.C, authz authorizer) { for _, hostPath := range []string{ arvadostest.FooCollection + ".example.com/foo", arvadostest.FooCollection + "--collections.example.com/foo", @@ -123,11 +183,11 @@ func doVhostRequests(c *check.C, authz authorizer) { arvadostest.FooBarDirCollection + ".example.com/dir1/foo", } { c.Log("doRequests: ", hostPath) - doVhostRequestsWithHostPath(c, authz, hostPath) + s.doVhostRequestsWithHostPath(c, authz, hostPath) } } -func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) { +func (s *IntegrationSuite) doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) { for _, tok := range []string{ arvadostest.ActiveToken, arvadostest.ActiveToken[:15], @@ -144,7 +204,7 @@ func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) Header: http.Header{}, } failCode := authz(req, tok) - req, resp := doReq(req) + req, resp := s.doReq(req) code, body := resp.Code, resp.Body.String() // If the initial request had a (non-empty) token @@ -173,9 +233,9 @@ func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) } } -func doReq(req *http.Request) (*http.Request, *httptest.ResponseRecorder) { +func (s *IntegrationSuite) doReq(req *http.Request) (*http.Request, *httptest.ResponseRecorder) { resp := httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) + s.testServer.Handler.ServeHTTP(resp, req) if resp.Code != http.StatusSeeOther { return req, resp } @@ -191,7 +251,7 @@ func doReq(req *http.Request) (*http.Request, *httptest.ResponseRecorder) { for _, c := range cookies { req.AddCookie(c) } - return doReq(req) + return s.doReq(req) } func (s *IntegrationSuite) TestVhostRedirectQueryTokenToCookie(c *check.C) { @@ -270,10 +330,7 @@ func (s *IntegrationSuite) TestVhostRedirectQueryTokenRequestAttachment(c *check } func (s *IntegrationSuite) TestVhostRedirectQueryTokenTrustAllContent(c *check.C) { - defer func(orig bool) { - trustAllContent = orig - }(trustAllContent) - trustAllContent = true + s.testServer.Config.TrustAllContent = true s.testVhostRedirectTokenToCookie(c, "GET", "example.com/c="+arvadostest.FooCollection+"/foo", "?api_token="+arvadostest.ActiveToken, @@ -285,10 +342,7 @@ func (s *IntegrationSuite) TestVhostRedirectQueryTokenTrustAllContent(c *check.C } func (s *IntegrationSuite) TestVhostRedirectQueryTokenAttachmentOnlyHost(c *check.C) { - defer func(orig string) { - attachmentOnlyHost = orig - }(attachmentOnlyHost) - attachmentOnlyHost = "example.com:1234" + s.testServer.Config.AttachmentOnlyHost = "example.com:1234" s.testVhostRedirectTokenToCookie(c, "GET", "example.com/c="+arvadostest.FooCollection+"/foo", @@ -333,7 +387,7 @@ func (s *IntegrationSuite) TestVhostRedirectPOSTFormTokenToCookie404(c *check.C) } func (s *IntegrationSuite) TestAnonymousTokenOK(c *check.C) { - anonymousTokens = []string{arvadostest.AnonymousToken} + s.testServer.Config.AnonymousTokens = []string{arvadostest.AnonymousToken} s.testVhostRedirectTokenToCookie(c, "GET", "example.com/c="+arvadostest.HelloWorldCollection+"/Hello%20world.txt", "", @@ -345,7 +399,7 @@ func (s *IntegrationSuite) TestAnonymousTokenOK(c *check.C) { } func (s *IntegrationSuite) TestAnonymousTokenError(c *check.C) { - anonymousTokens = []string{"anonymousTokenConfiguredButInvalid"} + s.testServer.Config.AnonymousTokens = []string{"anonymousTokenConfiguredButInvalid"} s.testVhostRedirectTokenToCookie(c, "GET", "example.com/c="+arvadostest.HelloWorldCollection+"/Hello%20world.txt", "", @@ -356,48 +410,6 @@ func (s *IntegrationSuite) TestAnonymousTokenError(c *check.C) { ) } -func (s *IntegrationSuite) TestRange(c *check.C) { - u, _ := url.Parse("http://example.com/c=" + arvadostest.HelloWorldCollection + "/Hello%20world.txt") - req := &http.Request{ - Method: "GET", - Host: u.Host, - URL: u, - RequestURI: u.RequestURI(), - Header: http.Header{"Range": {"bytes=0-4"}}, - } - resp := httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) - c.Check(resp.Code, check.Equals, http.StatusPartialContent) - c.Check(resp.Body.String(), check.Equals, "Hello") - c.Check(resp.Header().Get("Content-Length"), check.Equals, "5") - c.Check(resp.Header().Get("Content-Range"), check.Equals, "bytes 0-4/12") - - req.Header.Set("Range", "bytes=0-") - resp = httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) - // 200 and 206 are both correct: - c.Check(resp.Code, check.Equals, http.StatusOK) - c.Check(resp.Body.String(), check.Equals, "Hello world\n") - c.Check(resp.Header().Get("Content-Length"), check.Equals, "12") - - // Unsupported ranges are ignored - for _, hdr := range []string{ - "bytes=5-5", // non-zero start byte - "bytes=-5", // last 5 bytes - "cubits=0-5", // unsupported unit - "bytes=0-340282366920938463463374607431768211456", // 2^128 - } { - req.Header.Set("Range", hdr) - resp = httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) - c.Check(resp.Code, check.Equals, http.StatusOK) - c.Check(resp.Body.String(), check.Equals, "Hello world\n") - c.Check(resp.Header().Get("Content-Length"), check.Equals, "12") - c.Check(resp.Header().Get("Content-Range"), check.Equals, "") - c.Check(resp.Header().Get("Accept-Ranges"), check.Equals, "bytes") - } -} - // XHRs can't follow redirect-with-cookie so they rely on method=POST // and disposition=attachment (telling us it's acceptable to respond // with content instead of a redirect) and an Origin header that gets @@ -420,7 +432,7 @@ func (s *IntegrationSuite) TestXHRNoRedirect(c *check.C) { }.Encode())), } resp := httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) + s.testServer.Handler.ServeHTTP(resp, req) c.Check(resp.Code, check.Equals, http.StatusOK) c.Check(resp.Body.String(), check.Equals, "foo") c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*") @@ -443,7 +455,7 @@ func (s *IntegrationSuite) testVhostRedirectTokenToCookie(c *check.C, method, ho c.Check(resp.Body.String(), check.Equals, expectRespBody) }() - (&handler{}).ServeHTTP(resp, req) + s.testServer.Handler.ServeHTTP(resp, req) if resp.Code != http.StatusSeeOther { return resp } @@ -463,7 +475,7 @@ func (s *IntegrationSuite) testVhostRedirectTokenToCookie(c *check.C, method, ho } resp = httptest.NewRecorder() - (&handler{}).ServeHTTP(resp, req) + s.testServer.Handler.ServeHTTP(resp, req) c.Check(resp.Header().Get("Location"), check.Equals, "") return resp }