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) TestVhostRedirectPOSTFormTokenToCookie(c *check.C) {
181 s.testVhostRedirectTokenToCookie(c, "POST",
182 arvadostest.FooCollection + ".example.com/foo",
184 "application/x-www-form-urlencoded",
185 url.Values{"api_token": {arvadostest.ActiveToken}}.Encode(),
190 func (s *IntegrationSuite) TestVhostRedirectPOSTFormTokenToCookie404(c *check.C) {
191 s.testVhostRedirectTokenToCookie(c, "POST",
192 arvadostest.FooCollection + ".example.com/foo",
194 "application/x-www-form-urlencoded",
195 url.Values{"api_token": {arvadostest.SpectatorToken}}.Encode(),
200 func (s *IntegrationSuite) testVhostRedirectTokenToCookie(c *check.C, method, hostPath, queryString, contentType, body string, expectStatus int) {
201 u, _ := url.Parse(`http://` + hostPath + queryString)
202 req := &http.Request{
206 Header: http.Header{"Content-Type": {contentType}},
207 Body: ioutil.NopCloser(strings.NewReader(body)),
210 resp := httptest.NewRecorder()
211 (&handler{}).ServeHTTP(resp, req)
212 c.Assert(resp.Code, check.Equals, http.StatusSeeOther)
213 c.Check(resp.Body.String(), check.Matches, `.*href="//` + regexp.QuoteMeta(html.EscapeString(hostPath)) + `".*`)
214 cookies := (&http.Response{Header: resp.Header()}).Cookies()
216 u, _ = u.Parse(resp.Header().Get("Location"))
221 Header: http.Header{},
223 for _, c := range cookies {
227 resp = httptest.NewRecorder()
228 (&handler{}).ServeHTTP(resp, req)
229 c.Check(resp.Header().Get("Location"), check.Equals, "")
230 c.Check(resp.Code, check.Equals, expectStatus)
231 if expectStatus == http.StatusOK {
232 c.Check(resp.Body.String(), check.Equals, "foo")