19428: Fix failing request on normal failed user lookup case.
[arvados.git] / services / keep-web / handler.go
index 1f1f509860bb9950d95e5d9c566e9e57f9d4df36..0c75ac56cfc4d69d5845ec332196942d5998cdf7 100644 (file)
@@ -6,6 +6,7 @@ package keepweb
 
 import (
        "encoding/json"
+       "errors"
        "fmt"
        "html"
        "html/template"
@@ -486,7 +487,17 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 
        // Check configured permission
        _, sess, err := h.Cache.GetSession(arv.ApiToken)
+       if err != nil {
+               http.Error(w, "session cache: "+err.Error(), http.StatusInternalServerError)
+       }
        tokenUser, err = h.Cache.GetTokenUser(arv.ApiToken)
+       if e := (interface{ HTTPStatus() int })(nil); errors.As(err, &e) && e.HTTPStatus() == http.StatusForbidden {
+               // Ignore expected error looking up user record when
+               // using a scoped token that allows getting
+               // collections/X but not users/current
+       } else if err != nil {
+               http.Error(w, "user lookup: "+err.Error(), http.StatusInternalServerError)
+       }
 
        if webdavMethod[r.Method] {
                if !h.userPermittedToUploadOrDownload(r.Method, tokenUser) {
@@ -909,17 +920,18 @@ func (h *handler) logUploadOrDownload(
                collection, filepath = h.determineCollection(fs, filepath)
        }
        if collection != nil {
-               log = log.WithField("collection_uuid", collection.UUID).
-                       WithField("collection_file_path", filepath)
-               props["collection_uuid"] = collection.UUID
+               log = log.WithField("collection_file_path", filepath)
                props["collection_file_path"] = filepath
-               // h.determineCollection populates the collection_uuid prop with the PDH, if
-               // this collection is being accessed via PDH. In that case, blank the
-               // collection_uuid field so that consumers of the log entries can rely on it
-               // being a UUID, or blank. The PDH remains available via the
-               // portable_data_hash property.
-               if props["collection_uuid"] == collection.PortableDataHash {
-                       props["collection_uuid"] = ""
+               // h.determineCollection populates the collection_uuid
+               // prop with the PDH, if this collection is being
+               // accessed via PDH. For logging, we use a different
+               // field depending on whether it's a UUID or PDH.
+               if len(collection.UUID) > 32 {
+                       log = log.WithField("portable_data_hash", collection.UUID)
+                       props["portable_data_hash"] = collection.UUID
+               } else {
+                       log = log.WithField("collection_uuid", collection.UUID)
+                       props["collection_uuid"] = collection.UUID
                }
        }
        if r.Method == "PUT" || r.Method == "POST" {
@@ -958,29 +970,27 @@ func (h *handler) logUploadOrDownload(
 }
 
 func (h *handler) determineCollection(fs arvados.CustomFileSystem, path string) (*arvados.Collection, string) {
-       segments := strings.Split(path, "/")
-       var i int
-       for i = 0; i < len(segments); i++ {
-               dir := append([]string{}, segments[0:i]...)
-               dir = append(dir, ".arvados#collection")
-               f, err := fs.OpenFile(strings.Join(dir, "/"), os.O_RDONLY, 0)
-               if f != nil {
-                       defer f.Close()
-               }
+       target := strings.TrimSuffix(path, "/")
+       for {
+               fi, err := fs.Stat(target)
                if err != nil {
-                       if !os.IsNotExist(err) {
+                       return nil, ""
+               }
+               switch src := fi.Sys().(type) {
+               case *arvados.Collection:
+                       return src, strings.TrimPrefix(path[len(target):], "/")
+               case *arvados.Group:
+                       return nil, ""
+               default:
+                       if _, ok := src.(error); ok {
                                return nil, ""
                        }
-                       continue
                }
-               // err is nil so we found it.
-               decoder := json.NewDecoder(f)
-               var collection arvados.Collection
-               err = decoder.Decode(&collection)
-               if err != nil {
+               // Try parent
+               cut := strings.LastIndexByte(target, '/')
+               if cut < 0 {
                        return nil, ""
                }
-               return &collection, strings.Join(segments[i:], "/")
+               target = target[:cut]
        }
-       return nil, ""
 }