12 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
13 "git.curoverse.com/arvados.git/sdk/go/auth"
14 check "gopkg.in/check.v1"
17 var _ = check.Suite(&UnitSuite{})
19 type UnitSuite struct{}
21 func mustParseURL(s string) *url.URL {
22 r, err := url.Parse(s)
24 panic("parse URL: " + s)
29 func (s *IntegrationSuite) TestVhost404(c *check.C) {
30 for _, testURL := range []string{
31 arvadostest.NonexistentCollection + ".example.com/theperthcountyconspiracy",
32 arvadostest.NonexistentCollection + ".example.com/t=" + arvadostest.ActiveToken + "/theperthcountyconspiracy",
34 resp := httptest.NewRecorder()
37 URL: mustParseURL(testURL),
39 (&handler{}).ServeHTTP(resp, req)
40 c.Check(resp.Code, check.Equals, http.StatusNotFound)
41 c.Check(resp.Body.String(), check.Equals, "")
45 // An authorizer modifies an HTTP request to make use of the given
46 // token -- by adding it to a header, cookie, query param, or whatever
47 // -- and returns the HTTP status code we should expect from keep-web if
48 // the token is invalid.
49 type authorizer func(*http.Request, string) int
51 func (s *IntegrationSuite) TestVhostViaAuthzHeader(c *check.C) {
52 doVhostRequests(c, authzViaAuthzHeader)
54 func authzViaAuthzHeader(r *http.Request, tok string) int {
55 r.Header.Add("Authorization", "OAuth2 "+tok)
56 return http.StatusUnauthorized
59 func (s *IntegrationSuite) TestVhostViaCookieValue(c *check.C) {
60 doVhostRequests(c, authzViaCookieValue)
62 func authzViaCookieValue(r *http.Request, tok string) int {
63 r.AddCookie(&http.Cookie{
65 Value: auth.EncodeTokenCookie([]byte(tok)),
67 return http.StatusUnauthorized
70 func (s *IntegrationSuite) TestVhostViaPath(c *check.C) {
71 doVhostRequests(c, authzViaPath)
73 func authzViaPath(r *http.Request, tok string) int {
74 r.URL.Path = "/t=" + tok + r.URL.Path
75 return http.StatusNotFound
78 func (s *IntegrationSuite) TestVhostViaQueryString(c *check.C) {
79 doVhostRequests(c, authzViaQueryString)
81 func authzViaQueryString(r *http.Request, tok string) int {
82 r.URL.RawQuery = "api_token=" + tok
83 return http.StatusUnauthorized
86 func (s *IntegrationSuite) TestVhostViaPOST(c *check.C) {
87 doVhostRequests(c, authzViaPOST)
89 func authzViaPOST(r *http.Request, tok string) int {
91 r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
92 r.Body = ioutil.NopCloser(strings.NewReader(
93 url.Values{"api_token": {tok}}.Encode()))
94 return http.StatusUnauthorized
97 // Try some combinations of {url, token} using the given authorization
98 // mechanism, and verify the result is correct.
99 func doVhostRequests(c *check.C, authz authorizer) {
100 for _, hostPath := range []string{
101 arvadostest.FooCollection + ".example.com/foo",
102 arvadostest.FooCollection + "--dl.example.com/foo",
103 arvadostest.FooCollection + "--dl.example.com/_/foo",
104 arvadostest.FooPdh + ".example.com/foo",
105 strings.Replace(arvadostest.FooPdh, "+", "-", -1) + "--dl.example.com/foo",
107 c.Log("doRequests: ", hostPath)
108 doVhostRequestsWithHostPath(c, authz, hostPath)
112 func doVhostRequestsWithHostPath(c *check.C, authz authorizer, hostPath string) {
113 for _, tok := range []string{
114 arvadostest.ActiveToken,
115 arvadostest.ActiveToken[:15],
116 arvadostest.SpectatorToken,
120 u := mustParseURL("http://" + hostPath)
121 req := &http.Request{
125 Header: http.Header{},
127 failCode := authz(req, tok)
129 code, body := resp.Code, resp.Body.String()
130 if tok == arvadostest.ActiveToken {
131 c.Check(code, check.Equals, http.StatusOK)
132 c.Check(body, check.Equals, "foo")
134 c.Check(code >= 400, check.Equals, true)
135 c.Check(code < 500, check.Equals, true)
136 if tok == arvadostest.SpectatorToken {
137 // Valid token never offers to retry
138 // with different credentials.
139 c.Check(code, check.Equals, http.StatusNotFound)
141 // Invalid token can ask to retry
142 // depending on the authz method.
143 c.Check(code, check.Equals, failCode)
145 c.Check(body, check.Equals, "")
150 func doReq(req *http.Request) *httptest.ResponseRecorder {
151 resp := httptest.NewRecorder()
152 (&handler{}).ServeHTTP(resp, req)
153 if resp.Code != http.StatusSeeOther {
156 cookies := (&http.Response{Header: resp.Header()}).Cookies()
157 u, _ := req.URL.Parse(resp.Header().Get("Location"))
162 Header: http.Header{},
164 for _, c := range cookies {
170 func (s *IntegrationSuite) TestVhostRedirectQueryTokenToCookie(c *check.C) {
171 s.testVhostRedirectTokenToCookie(c, "GET",
172 arvadostest.FooCollection+".example.com/foo",
173 "?api_token="+arvadostest.ActiveToken,
180 func (s *IntegrationSuite) TestVhostRedirectQueryTokenSingleOriginError(c *check.C) {
181 s.testVhostRedirectTokenToCookie(c, "GET",
182 "example.com/c="+arvadostest.FooCollection+"/foo",
183 "?api_token="+arvadostest.ActiveToken,
186 http.StatusBadRequest,
190 func (s *IntegrationSuite) TestVhostRedirectQueryTokenTrustAllContent(c *check.C) {
191 defer func(orig bool) {
192 trustAllContent = orig
194 trustAllContent = true
195 s.testVhostRedirectTokenToCookie(c, "GET",
196 "example.com/c="+arvadostest.FooCollection+"/foo",
197 "?api_token="+arvadostest.ActiveToken,
204 func (s *IntegrationSuite) TestVhostRedirectQueryTokenAttachmentOnlyHost(c *check.C) {
205 defer func(orig string) {
206 attachmentOnlyHost = orig
207 }(attachmentOnlyHost)
208 attachmentOnlyHost = "example.com:1234"
210 s.testVhostRedirectTokenToCookie(c, "GET",
211 "example.com/c="+arvadostest.FooCollection+"/foo",
212 "?api_token="+arvadostest.ActiveToken,
215 http.StatusBadRequest,
218 resp := s.testVhostRedirectTokenToCookie(c, "GET",
219 "example.com:1234/c="+arvadostest.FooCollection+"/foo",
220 "?api_token="+arvadostest.ActiveToken,
225 c.Check(resp.Header().Get("Content-Disposition"), check.Equals, "attachment")
228 func (s *IntegrationSuite) TestVhostRedirectPOSTFormTokenToCookie(c *check.C) {
229 s.testVhostRedirectTokenToCookie(c, "POST",
230 arvadostest.FooCollection+".example.com/foo",
232 "application/x-www-form-urlencoded",
233 url.Values{"api_token": {arvadostest.ActiveToken}}.Encode(),
238 func (s *IntegrationSuite) TestVhostRedirectPOSTFormTokenToCookie404(c *check.C) {
239 s.testVhostRedirectTokenToCookie(c, "POST",
240 arvadostest.FooCollection+".example.com/foo",
242 "application/x-www-form-urlencoded",
243 url.Values{"api_token": {arvadostest.SpectatorToken}}.Encode(),
248 func (s *IntegrationSuite) testVhostRedirectTokenToCookie(c *check.C, method, hostPath, queryString, contentType, body string, expectStatus int) *httptest.ResponseRecorder {
249 u, _ := url.Parse(`http://` + hostPath + queryString)
250 req := &http.Request{
254 Header: http.Header{"Content-Type": {contentType}},
255 Body: ioutil.NopCloser(strings.NewReader(body)),
258 resp := httptest.NewRecorder()
259 (&handler{}).ServeHTTP(resp, req)
260 if resp.Code != http.StatusSeeOther {
261 c.Assert(resp.Code, check.Equals, expectStatus)
264 c.Check(resp.Body.String(), check.Matches, `.*href="//`+regexp.QuoteMeta(html.EscapeString(hostPath))+`".*`)
265 cookies := (&http.Response{Header: resp.Header()}).Cookies()
267 u, _ = u.Parse(resp.Header().Get("Location"))
272 Header: http.Header{},
274 for _, c := range cookies {
278 resp = httptest.NewRecorder()
279 (&handler{}).ServeHTTP(resp, req)
280 c.Check(resp.Header().Get("Location"), check.Equals, "")
281 c.Check(resp.Code, check.Equals, expectStatus)
282 if expectStatus == http.StatusOK {
283 c.Check(resp.Body.String(), check.Equals, "foo")