}
func CheckAuthorizationHeader(kc *keepclient.KeepClient, cache *ApiTokenCache, req *http.Request) (pass bool, tok string) {
- var auth string
- if auth = req.Header.Get("Authorization"); auth == "" {
+ parts := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
+ if len(parts) < 2 || !(parts[0] == "OAuth2" || parts[0] == "Bearer") || len(parts[1]) == 0 {
return false, ""
}
+ tok = parts[1]
- _, err := fmt.Sscanf(auth, "OAuth2 %s", &tok)
- if err != nil {
- // Scanning error
- return false, ""
+ // Tokens are validated differently depending on what kind of
+ // operation is being performed. For example, tokens in
+ // collection-sharing links permit GET requests, but not
+ // PUT requests.
+ var op string
+ if req.Method == "GET" || req.Method == "HEAD" {
+ op = "read"
+ } else {
+ op = "write"
}
- if cache.RecallToken(tok) {
+ if cache.RecallToken(op + ":" + tok) {
// Valid in the cache, short circuit
return true, tok
}
+ var err error
arv := *kc.Arvados
arv.ApiToken = tok
- if err := arv.Call("HEAD", "users", "", "current", nil, nil); err != nil {
+ if op == "read" {
+ err = arv.Call("HEAD", "keep_services", "", "accessible", nil, nil)
+ } else {
+ err = arv.Call("HEAD", "users", "", "current", nil, nil)
+ }
+ if err != nil {
log.Printf("%s: CheckAuthorizationHeader error: %v", GetRemoteAddress(req), err)
return false, ""
}
// Success! Update cache
- cache.RememberToken(tok)
+ cache.RememberToken(op + ":" + tok)
return true, tok
}
kc := runProxy(c, nil, true)
defer closeListener()
- hash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
+ hash := fmt.Sprintf("%x+3", md5.Sum([]byte("bar")))
- {
- _, _, err := kc.Ask(hash)
- errNotFound, _ := err.(keepclient.ErrNotFound)
- c.Check(errNotFound, NotNil)
- c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
- c.Log("Ask 1")
- }
+ _, _, err := kc.Ask(hash)
+ c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
- {
- hash2, rep, err := kc.PutB([]byte("bar"))
- c.Check(hash2, Equals, "")
- c.Check(rep, Equals, 0)
- c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
- c.Log("PutB")
- }
+ hash2, rep, err := kc.PutB([]byte("bar"))
+ c.Check(hash2, Equals, "")
+ c.Check(rep, Equals, 0)
+ c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
- {
- blocklen, _, err := kc.Ask(hash)
- errNotFound, _ := err.(keepclient.ErrNotFound)
- c.Check(errNotFound, NotNil)
- c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
- c.Check(blocklen, Equals, int64(0))
- c.Log("Ask 2")
- }
+ blocklen, _, err := kc.Ask(hash)
+ c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
+ c.Check(err, ErrorMatches, ".*not found.*")
+ c.Check(blocklen, Equals, int64(0))
+
+ _, blocklen, _, err = kc.Get(hash)
+ c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
+ c.Check(err, ErrorMatches, ".*not found.*")
+ c.Check(blocklen, Equals, int64(0))
- {
- _, blocklen, _, err := kc.Get(hash)
- errNotFound, _ := err.(keepclient.ErrNotFound)
- c.Check(errNotFound, NotNil)
- c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
- c.Check(blocklen, Equals, int64(0))
- c.Log("Get")
- }
}
func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
c.Assert((err != nil), Equals, true)
}
+func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
+ kc := runProxy(c, nil, false)
+ defer closeListener()
+ hash, _, err := kc.PutB([]byte("shareddata"))
+ c.Check(err, IsNil)
+ kc.Arvados.ApiToken = arvadostest.FooCollectionSharingToken
+ rdr, _, _, err := kc.Get(hash)
+ c.Assert(err, IsNil)
+ data, err := ioutil.ReadAll(rdr)
+ c.Check(err, IsNil)
+ c.Check(data, DeepEquals, []byte("shareddata"))
+}
+
func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
kc := runProxy(c, nil, false)
defer closeListener()
// Put a test block
hash, rep, err := kc.PutB([]byte("foo"))
- c.Check(err, Equals, nil)
+ c.Check(err, IsNil)
c.Check(rep, Equals, 2)
- for _, token := range []string{
+ for _, badToken := range []string{
"nosuchtoken",
"2ym314ysp27sk7h943q6vtc378srb06se3pq6ghurylyf3pdmx", // expired
} {
- // Change token to given bad token
- kc.Arvados.ApiToken = token
+ kc.Arvados.ApiToken = badToken
+
+ // Ask and Get will fail only if the upstream
+ // keepstore server checks for valid signatures.
+ // Without knowing the blob signing key, there is no
+ // way for keepproxy to know whether a given token is
+ // permitted to read a block. So these tests fail:
+ if false {
+ _, _, err = kc.Ask(hash)
+ c.Assert(err, FitsTypeOf, &keepclient.ErrNotFound{})
+ c.Check(err.(*keepclient.ErrNotFound).Temporary(), Equals, false)
+ c.Check(err, ErrorMatches, ".*HTTP 403.*")
+
+ _, _, _, err = kc.Get(hash)
+ c.Assert(err, FitsTypeOf, &keepclient.ErrNotFound{})
+ c.Check(err.(*keepclient.ErrNotFound).Temporary(), Equals, false)
+ c.Check(err, ErrorMatches, ".*HTTP 403 \"Missing or invalid Authorization header\".*")
+ }
- // Ask should result in error
- _, _, err = kc.Ask(hash)
- c.Check(err, NotNil)
- errNotFound, _ := err.(keepclient.ErrNotFound)
- c.Check(errNotFound.Temporary(), Equals, false)
- c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
-
- // Get should result in error
- _, _, _, err = kc.Get(hash)
- c.Check(err, NotNil)
- errNotFound, _ = err.(keepclient.ErrNotFound)
- c.Check(errNotFound.Temporary(), Equals, false)
- c.Assert(strings.Contains(err.Error(), "HTTP 403 \"Missing or invalid Authorization header\""), Equals, true)
+ _, _, err = kc.PutB([]byte("foo"))
+ c.Check(err, ErrorMatches, ".*403.*Missing or invalid Authorization header")
}
}