import (
"encoding/json"
+ "fmt"
"html"
"html/template"
"io"
}
)
+func stripDefaultPort(host string) string {
+ // Will consider port 80 and port 443 to be the same vhost. I think that's fine.
+ u := &url.URL{Host: host}
+ if p := u.Port(); p == "80" || p == "443" {
+ return u.Hostname()
+ } else {
+ return host
+ }
+}
+
// ServeHTTP implements http.Handler.
func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
h.setupOnce.Do(h.setup)
var attachment bool
var useSiteFS bool
credentialsOK := h.Config.cluster.Collections.TrustAllContent
+ reasonNotAcceptingCredentials := ""
- if r.Host != "" && r.Host == h.Config.cluster.Services.WebDAVDownload.ExternalURL.Host {
+ if r.Host != "" && stripDefaultPort(r.Host) == stripDefaultPort(h.Config.cluster.Services.WebDAVDownload.ExternalURL.Host) {
credentialsOK = true
attachment = true
} else if r.FormValue("disposition") == "attachment" {
attachment = true
}
+ if !credentialsOK {
+ reasonNotAcceptingCredentials = fmt.Sprintf("vhost %q does not specify a single collection ID or match Services.WebDAVDownload.ExternalURL %q, and Collections.TrustAllContent is false",
+ r.Host, h.Config.cluster.Services.WebDAVDownload.ExternalURL)
+ }
+
if collectionID = parseCollectionIDFromDNSName(r.Host); collectionID != "" {
// http://ID.collections.example/PATH...
credentialsOK = true
// data. Tokens provided with the request are
// ignored.
credentialsOK = false
+ reasonNotAcceptingCredentials = "the '/collections/UUID/PATH' form only works for public data"
}
}
}
if tokens == nil {
- tokens = append(reqTokens, h.Config.cluster.Users.AnonymousUserToken)
+ tokens = reqTokens
+ if h.Config.cluster.Users.AnonymousUserToken != "" {
+ tokens = append(tokens, h.Config.cluster.Users.AnonymousUserToken)
+ }
+ }
+
+ if tokens == nil {
+ if !credentialsOK {
+ http.Error(w, fmt.Sprintf("Authorization tokens are not accepted here: %v, and no anonymous user token is configured.", reasonNotAcceptingCredentials), http.StatusUnauthorized)
+ } else {
+ http.Error(w, fmt.Sprintf("No authorization token in request, and no anonymous user token is configured."), http.StatusUnauthorized)
+ }
+ return
}
if len(targetPath) > 0 && targetPath[0] == "_" {
// the token is invalid.
type authorizer func(*http.Request, string) int
-func (s *IntegrationSuite) TestVhostViaAuthzHeader(c *check.C) {
- s.doVhostRequests(c, authzViaAuthzHeader)
+func (s *IntegrationSuite) TestVhostViaAuthzHeaderOAuth2(c *check.C) {
+ s.doVhostRequests(c, authzViaAuthzHeaderOAuth2)
}
-func authzViaAuthzHeader(r *http.Request, tok string) int {
- r.Header.Add("Authorization", "OAuth2 "+tok)
+func authzViaAuthzHeaderOAuth2(r *http.Request, tok string) int {
+ r.Header.Add("Authorization", "Bearer "+tok)
+ return http.StatusUnauthorized
+}
+func (s *IntegrationSuite) TestVhostViaAuthzHeaderBearer(c *check.C) {
+ s.doVhostRequests(c, authzViaAuthzHeaderBearer)
+}
+func authzViaAuthzHeaderBearer(r *http.Request, tok string) int {
+ r.Header.Add("Authorization", "Bearer "+tok)
return http.StatusUnauthorized
}
}
}
+func (s *IntegrationSuite) TestVhostPortMatch(c *check.C) {
+ for _, port := range []string{"80", "443", "8000"} {
+ s.testServer.Config.cluster.Services.WebDAVDownload.ExternalURL.Host = fmt.Sprintf("download.example.com:%v", port)
+ u := mustParseURL(fmt.Sprintf("http://download.example.com/by_id/%v/foo", arvadostest.FooCollection))
+ req := &http.Request{
+ Method: "GET",
+ Host: u.Host,
+ URL: u,
+ RequestURI: u.RequestURI(),
+ Header: http.Header{"Authorization": []string{"Bearer " + arvadostest.ActiveToken}},
+ }
+ req, resp := s.doReq(req)
+ code, _ := resp.Code, resp.Body.String()
+
+ if port == "8000" {
+ c.Check(code, check.Equals, 401)
+ } else {
+ c.Check(code, check.Equals, 200)
+ }
+ }
+}
+
func (s *IntegrationSuite) doReq(req *http.Request) (*http.Request, *httptest.ResponseRecorder) {
resp := httptest.NewRecorder()
s.testServer.Handler.ServeHTTP(resp, req)
{
// URLs of this form ignore authHeader, and
// FooAndBarFilesInDirUUID isn't public, so
- // this returns 404.
+ // this returns 401.
uri: "download.example.com/collections/" + arvadostest.FooAndBarFilesInDirUUID + "/",
header: authHeader,
expect: nil,
c.Check(req.URL.Path, check.Equals, trial.redirect, comment)
}
if trial.expect == nil {
- c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ if s.testServer.Config.cluster.Users.AnonymousUserToken == "" {
+ c.Check(resp.Code, check.Equals, http.StatusUnauthorized, comment)
+ } else {
+ c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ }
} else {
c.Check(resp.Code, check.Equals, http.StatusOK, comment)
for _, e := range trial.expect {
resp = httptest.NewRecorder()
s.testServer.Handler.ServeHTTP(resp, req)
if trial.expect == nil {
- c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ if s.testServer.Config.cluster.Users.AnonymousUserToken == "" {
+ c.Check(resp.Code, check.Equals, http.StatusUnauthorized, comment)
+ } else {
+ c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ }
} else {
c.Check(resp.Code, check.Equals, http.StatusOK, comment)
}
resp = httptest.NewRecorder()
s.testServer.Handler.ServeHTTP(resp, req)
if trial.expect == nil {
- c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ if s.testServer.Config.cluster.Users.AnonymousUserToken == "" {
+ c.Check(resp.Code, check.Equals, http.StatusUnauthorized, comment)
+ } else {
+ c.Check(resp.Code, check.Equals, http.StatusNotFound, comment)
+ }
} else {
c.Check(resp.Code, check.Equals, http.StatusMultiStatus, comment)
for _, e := range trial.expect {