diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index 61fd21fb39..7fff813688 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -67,6 +67,7 @@ type config struct { AvailableXS map[string]uint32 `docs:"nil;List of available checksums." mapstructure:"available_checksums"` CustomMimeTypesJSON string `docs:"nil;An optional mapping file with the list of supported custom file extensions and corresponding mime types." mapstructure:"custom_mime_types_json"` MinimunAllowedPathLevelForShare int `mapstructure:"minimum_allowed_path_level_for_share"` + SpaceLevel int `mapstructure:"space_level"` } func (c *config) ApplyDefaults() { @@ -91,6 +92,10 @@ func (c *config) ApplyDefaults() { } } + if c.SpaceLevel == 0 { + c.SpaceLevel = 4 + } + // set sane defaults if len(c.AvailableXS) == 0 { c.AvailableXS = map[string]uint32{"md5": 100, "unset": 1000} @@ -758,6 +763,22 @@ func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provide return res, nil } +func spaceFromPath(path string, lvl int) string { + path = strings.TrimPrefix(path, "/") + s := strings.SplitN(path, "/", lvl+1) + if len(s) < lvl { + // TODO: outside space. what to do?? + panic("not yet implemented") + } + + return "/" + strings.Join(s[:lvl], "/") +} + +func (s *service) addSpaceInfo(ri *provider.ResourceInfo) { + space := spaceFromPath(ri.Path, s.conf.SpaceLevel) + ri.Id.SpaceId = space +} + func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) { newRef, err := s.unwrap(ctx, req.Ref) if err != nil { @@ -789,6 +810,7 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide }, nil } s.fixPermissions(md) + s.addSpaceInfo(md) res := &provider.StatResponse{ Status: status.NewOK(ctx), Info: md, @@ -949,6 +971,7 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer }, nil } s.fixPermissions(md) + s.addSpaceInfo(md) infos = append(infos, md) } res := &provider.ListContainerResponse{ diff --git a/internal/http/services/owncloud/ocdav/dav.go b/internal/http/services/owncloud/ocdav/dav.go index 930142f34a..1157439730 100644 --- a/internal/http/services/owncloud/ocdav/dav.go +++ b/internal/http/services/owncloud/ocdav/dav.go @@ -178,26 +178,34 @@ func (h *DavHandler) Handler(s *svc) http.Handler { base := path.Join(ctx.Value(ctxKeyBaseURI).(string), "spaces") ctx := context.WithValue(ctx, ctxKeyBaseURI, base) - // path is of type: space_id/relative/path/from/space - // the space_id is the base64 encode of the path where - // the space is located - spaceID, relativeSpacePath := router.ShiftPath(r.URL.Path) - - _, base, ok := spaces.DecodeSpaceID(spaceID) - if !ok { - // TODO: bad request - panic("not yet implemented") - } + var head string + head, r.URL.Path = router.ShiftPath(r.URL.Path) - fullPath := filepath.Join(base, relativeSpacePath) - r.URL.Path = fullPath + switch head { + case "trash-bin": + r = r.WithContext(ctx) + h.TrashbinHandler.Handler(s).ServeHTTP(w, r) + default: + // path is of type: space_id/relative/path/from/space + // the space_id is the base64 encode of the path where + // the space is located + + _, base, ok := spaces.DecodeSpaceID(head) + if !ok { + // TODO: bad request + panic("not yet implemented") + } - ctx = context.WithValue(ctx, ctxSpaceID, spaceID) - ctx = context.WithValue(ctx, ctxSpaceFullPath, fullPath) - ctx = context.WithValue(ctx, ctxSpacePath, base) - ctx = context.WithValue(ctx, ctxSpaceRelativePath, relativeSpacePath) - r = r.WithContext(ctx) - h.SpacesHandler.Handler(s).ServeHTTP(w, r) + fullPath := filepath.Join(base, r.URL.Path) + r.URL.Path = fullPath + + ctx = context.WithValue(ctx, ctxSpaceID, head) + ctx = context.WithValue(ctx, ctxSpaceFullPath, fullPath) + ctx = context.WithValue(ctx, ctxSpacePath, base) + ctx = context.WithValue(ctx, ctxSpaceRelativePath, r.URL.Path) + r = r.WithContext(ctx) + h.SpacesHandler.Handler(s).ServeHTTP(w, r) + } case "ocm": base := path.Join(ctx.Value(ctxKeyBaseURI).(string), "ocm") ctx := context.WithValue(ctx, ctxKeyBaseURI, base) diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 5d38253995..b806b1f8a6 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -343,12 +343,7 @@ func extractDestination(r *http.Request) (string, error) { baseURI := r.Context().Value(ctxKeyBaseURI).(string) // TODO check if path is on same storage, return 502 on problems, see https://tools.ietf.org/html/rfc4918#section-9.9.4 // Strip the base URI from the destination. The destination might contain redirection prefixes which need to be handled - urlSplit := strings.Split(dstURL.Path, baseURI) - if len(urlSplit) != 2 { - return "", errors.Wrap(errInvalidValue, "destination path does not contain base URI") - } - - return urlSplit[1], nil + return strings.TrimPrefix(dstURL.Path, baseURI), nil } // replaceAllStringSubmatchFunc is taken from 'Go: Replace String with Regular Expression Callback' diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go index ec25e91797..5b3bb35794 100644 --- a/internal/http/services/owncloud/ocdav/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind.go @@ -42,6 +42,7 @@ import ( "github.com/cs3org/reva/internal/grpc/services/storageprovider" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/spaces" "github.com/cs3org/reva/pkg/publicshare" "github.com/cs3org/reva/pkg/rhttp/router" @@ -626,7 +627,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide // return all known properties if md.Id != nil { - id := resourceid.OwnCloudResourceIDWrap(md.Id) + id := spaces.EncodeResourceID(md.Id) propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:id", id), s.newProp("oc:fileid", id), @@ -725,13 +726,13 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide // I tested the desktop client and phoenix to annotate which properties are requestted, see below cases case "fileid": // phoenix only if md.Id != nil { - propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:fileid", resourceid.OwnCloudResourceIDWrap(md.Id))) + propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:fileid", spaces.EncodeResourceID(md.Id))) } else { propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:fileid", "")) } case "id": // desktop client only if md.Id != nil { - propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:id", resourceid.OwnCloudResourceIDWrap(md.Id))) + propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:id", spaces.EncodeResourceID(md.Id))) } else { propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:id", "")) } diff --git a/internal/http/services/owncloud/ocdav/trashbin.go b/internal/http/services/owncloud/ocdav/trashbin.go index aae15ed25d..4f104d992f 100644 --- a/internal/http/services/owncloud/ocdav/trashbin.go +++ b/internal/http/services/owncloud/ocdav/trashbin.go @@ -20,7 +20,6 @@ package ocdav import ( "context" - "encoding/base32" "encoding/xml" "fmt" "net/http" @@ -34,6 +33,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/spaces" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/rhttp/router" @@ -44,7 +44,6 @@ import ( // TrashbinHandler handles trashbin requests. type TrashbinHandler struct { gatewaySvc string - spaces bool } func (h *TrashbinHandler) init(c *Config) error { @@ -56,24 +55,53 @@ func (h *TrashbinHandler) handleTrashbinSpaces(s *svc, w http.ResponseWriter, r ctx := r.Context() log := appctx.GetLogger(ctx) - space, _ := router.ShiftPath(r.URL.Path) + var spaceId string + spaceId, r.URL.Path = router.ShiftPath(r.URL.Path) - spaceId, err := base32.StdEncoding.DecodeString(space) - if err != nil { - log.Error().Err(err).Msgf("error decoding space id: %s", space) - w.WriteHeader(http.StatusBadRequest) - return + _, base, ok := spaces.DecodeSpaceID(spaceId) + if !ok { + // TODO: bad request + panic("not yet implemented: bad request") } - - path := string(spaceId) - log.Debug().Str("path", path).Msg("decoded space base path") + log.Debug().Str("path", base).Msg("decoded space base path") u := appctx.ContextMustGetUser(ctx) if r.Method == MethodPropfind { - h.listTrashbin(w, r, s, u, path, "", "") + h.listTrashbin(w, r, s, u, base, "", "") + return + } + + var key string + key, r.URL.Path = router.ShiftPath(r.URL.Path) + if key != "" && r.Method == MethodMove { + // find path in url relative to trash base + // trashBase := ctx.Value(ctxKeyBaseURI).(string) + // baseURI := path.Join(path.Dir(trashBase), "files", username) + // ctx = context.WithValue(ctx, ctxKeyBaseURI, baseURI) + // r = r.WithContext(ctx) + + // TODO make request.php optional in destination header + dst, err := extractDestination(r) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + dst = path.Clean(dst) + _, dst = router.ShiftPath(dst) + + log.Debug().Str("key", key).Str("dst", dst).Msg("restore") + + h.restore(w, r, s, u, base, dst, key, "") return } + + if r.Method == http.MethodDelete { + h.delete(w, r, s, u, base, key, "") + return + } + + http.Error(w, "501 Not implemented", http.StatusNotImplemented) } // Handler handles requests. diff --git a/pkg/spaces/utils.go b/pkg/spaces/utils.go index e69162d16b..048522b36d 100644 --- a/pkg/spaces/utils.go +++ b/pkg/spaces/utils.go @@ -48,13 +48,14 @@ func EncodeResourceID(r *provider.ResourceId) string { if r.OpaqueId == "" { panic("opaque id cannot be empty") } - // if r.SpaceId == "" { - // panic("space id cannot be empty") - // } + if r.SpaceId == "" { + panic("space id cannot be empty") + } if r.StorageId == "" { panic("storage id cannot be empty") } - return fmt.Sprintf("%s$%s!%s", r.StorageId, r.SpaceId, r.OpaqueId) + spaceID := EncodeSpaceID(r.StorageId, r.SpaceId) + return fmt.Sprintf("%s!%s", spaceID, r.OpaqueId) } // EncodeSpaceID encodes storage ID and path to create a space ID,