X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/58692c916bb6dfe2997838ca4147109d9410c86a..c3d4f8a585202ec58df5506934b698039c200b68:/services/keep-web/handler_test.go diff --git a/services/keep-web/handler_test.go b/services/keep-web/handler_test.go index 94a1cf7c07..0c960b8c0e 100644 --- a/services/keep-web/handler_test.go +++ b/services/keep-web/handler_test.go @@ -38,7 +38,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 +51,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 +59,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 +70,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 +78,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 +86,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" @@ -96,9 +96,24 @@ func authzViaPOST(r *http.Request, tok string) int { return http.StatusUnauthorized } +func (s *IntegrationSuite) TestVhostViaXHRPOST(c *check.C) { + s.doVhostRequests(c, authzViaPOST) +} +func authzViaXHRPOST(r *http.Request, tok string) int { + r.Method = "POST" + r.Header.Add("Content-Type", "application/x-www-form-urlencoded") + r.Header.Add("Origin", "https://origin.example") + r.Body = ioutil.NopCloser(strings.NewReader( + url.Values{ + "api_token": {tok}, + "disposition": {"attachment"}, + }.Encode())) + return http.StatusUnauthorized +} + // 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", @@ -108,11 +123,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], @@ -129,11 +144,18 @@ func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) Header: http.Header{}, } failCode := authz(req, tok) - resp := doReq(req) + req, resp := s.doReq(req) code, body := resp.Code, resp.Body.String() + + // If the initial request had a (non-empty) token + // showing in the query string, we should have been + // redirected in order to hide it in a cookie. + c.Check(req.URL.String(), check.Not(check.Matches), `.*api_token=.+`) + if tok == arvadostest.ActiveToken { c.Check(code, check.Equals, http.StatusOK) c.Check(body, check.Equals, "foo") + } else { c.Check(code >= 400, check.Equals, true) c.Check(code < 500, check.Equals, true) @@ -151,11 +173,11 @@ func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) } } -func doReq(req *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 resp + return req, resp } cookies := (&http.Response{Header: resp.Header()}).Cookies() u, _ := req.URL.Parse(resp.Header().Get("Location")) @@ -169,7 +191,7 @@ func doReq(req *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) { @@ -248,10 +270,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, @@ -263,10 +282,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", @@ -311,7 +327,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", "", @@ -323,7 +339,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", "", @@ -334,46 +350,32 @@ 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") +// 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 +// added automatically by the browser (telling us it's desirable to do +// so). +func (s *IntegrationSuite) TestXHRNoRedirect(c *check.C) { + u, _ := url.Parse("http://example.com/c=" + arvadostest.FooCollection + "/foo") req := &http.Request{ - Method: "GET", + Method: "POST", Host: u.Host, URL: u, RequestURI: u.RequestURI(), - Header: http.Header{"Range": {"bytes=0-4"}}, + Header: http.Header{ + "Origin": {"https://origin.example"}, + "Content-Type": {"application/x-www-form-urlencoded"}, + }, + Body: ioutil.NopCloser(strings.NewReader(url.Values{ + "api_token": {arvadostest.ActiveToken}, + "disposition": {"attachment"}, + }.Encode())), } 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: + s.testServer.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") - - // 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") - } + c.Check(resp.Body.String(), check.Equals, "foo") + c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*") } func (s *IntegrationSuite) testVhostRedirectTokenToCookie(c *check.C, method, hostPath, queryString, contentType, reqBody string, expectStatus int, expectRespBody string) *httptest.ResponseRecorder { @@ -393,7 +395,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 } @@ -413,7 +415,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 }