From 0164880ac7d345e6318814960ffeeaa3779cfa07 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Wed, 27 Apr 2022 22:33:55 +0200 Subject: [PATCH] Add support for meta-path-for-user propfind (#2741) * Add support for meta-path-for-user propfind * handle invalid resourceID Co-authored-by: Michael Barz --- changelog/unreleased/meta-path-for-user.md | 6 + internal/http/services/owncloud/ocdav/meta.go | 122 +++++++++++++++++- .../http/services/owncloud/ocdav/net/net.go | 2 + 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/meta-path-for-user.md diff --git a/changelog/unreleased/meta-path-for-user.md b/changelog/unreleased/meta-path-for-user.md new file mode 100644 index 0000000000..306e7baa3f --- /dev/null +++ b/changelog/unreleased/meta-path-for-user.md @@ -0,0 +1,6 @@ +Enhancement: Meta path for user + +We've added support for requesting the `meta-path-for-user` via a propfind to the `dav/meta/` endpoint. + +https://github.com/cs3org/reva/pull/2741 +https://doc.owncloud.com/server/next/developer_manual/webdav_api/meta.html diff --git a/internal/http/services/owncloud/ocdav/meta.go b/internal/http/services/owncloud/ocdav/meta.go index 0acc610ec5..ac2fe06288 100644 --- a/internal/http/services/owncloud/ocdav/meta.go +++ b/internal/http/services/owncloud/ocdav/meta.go @@ -19,9 +19,20 @@ package ocdav import ( + "encoding/xml" + "fmt" "net/http" + "path" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" + "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/rhttp/router" + rtrace "github.com/cs3org/reva/v2/pkg/trace" "github.com/cs3org/reva/v2/pkg/utils/resourceid" ) @@ -42,15 +53,30 @@ func (h *MetaHandler) Handler(s *svc) http.Handler { var id string id, r.URL.Path = router.ShiftPath(r.URL.Path) if id == "" { - http.Error(w, "400 Bad Request", http.StatusBadRequest) + w.WriteHeader(http.StatusBadRequest) return } did := resourceid.OwnCloudResourceIDUnwrap(id) + if did == nil { + logger := appctx.GetLogger(r.Context()) + logger.Debug().Str("prop", net.PropOcMetaPathForUser).Msg("invalid resource id") + w.WriteHeader(http.StatusBadRequest) + m := fmt.Sprintf("Invalid resource id %v", id) + b, err := errors.Marshal(http.StatusBadRequest, m, "") + errors.HandleWebdavError(logger, w, b, err) + return + } var head string head, r.URL.Path = router.ShiftPath(r.URL.Path) switch head { + case "": + if r.Method != MethodPropfind { + w.WriteHeader(http.StatusBadRequest) + return + } + h.handlePathForUser(w, r, s, did) case "v": h.VersionsHandler.Handler(s, did).ServeHTTP(w, r) default: @@ -59,3 +85,97 @@ func (h *MetaHandler) Handler(s *svc) http.Handler { }) } + +func (h *MetaHandler) handlePathForUser(w http.ResponseWriter, r *http.Request, s *svc, rid *provider.ResourceId) { + ctx, span := rtrace.Provider.Tracer("ocdav").Start(r.Context(), "meta_propfind") + defer span.End() + + id := resourceid.OwnCloudResourceIDWrap(rid) + sublog := appctx.GetLogger(ctx).With().Str("path", r.URL.Path).Str("resourceid", id).Logger() + client, err := s.getClient() + if err != nil { + sublog.Error().Err(err).Msg("error getting grpc client") + w.WriteHeader(http.StatusInternalServerError) + return + } + + pf, status, err := propfind.ReadPropfind(r.Body) + if err != nil { + sublog.Debug().Err(err).Msg("error reading propfind request") + w.WriteHeader(status) + return + } + + if ok := hasProp(&pf, net.PropOcMetaPathForUser); !ok { + sublog.Debug().Str("prop", net.PropOcMetaPathForUser).Msg("error finding prop in request") + w.WriteHeader(http.StatusBadRequest) + return + } + + pathReq := &provider.GetPathRequest{ResourceId: rid} + pathRes, err := client.GetPath(ctx, pathReq) + if err != nil { + sublog.Error().Err(err).Msg("error sending GetPath grpc request") + w.WriteHeader(http.StatusInternalServerError) + return + } + + switch pathRes.Status.Code { + case rpc.Code_CODE_NOT_FOUND: + sublog.Debug().Str("code", string(pathRes.Status.Code)).Msg("resource not found") + w.WriteHeader(http.StatusNotFound) + m := fmt.Sprintf("Resource %s not found", id) + b, err := errors.Marshal(http.StatusNotFound, m, "") + errors.HandleWebdavError(&sublog, w, b, err) + return + case rpc.Code_CODE_PERMISSION_DENIED: + // raise StatusNotFound so that resources can't be enumerated + sublog.Debug().Str("code", string(pathRes.Status.Code)).Msg("resource access denied") + w.WriteHeader(http.StatusNotFound) + m := fmt.Sprintf("Resource %s not found", id) + b, err := errors.Marshal(http.StatusNotFound, m, "") + errors.HandleWebdavError(&sublog, w, b, err) + return + } + + propstatOK := propfind.PropstatXML{ + Status: "HTTP/1.1 200 OK", + Prop: []prop.PropertyXML{ + prop.Escaped("oc:meta-path-for-user", pathRes.Path), + }, + } + baseURI := ctx.Value(net.CtxKeyBaseURI).(string) + msr := propfind.NewMultiStatusResponseXML() + msr.Responses = []*propfind.ResponseXML{ + { + Href: net.EncodePath(path.Join(baseURI, id) + "/"), + Propstat: []propfind.PropstatXML{ + propstatOK, + }, + }, + } + propRes, err := xml.Marshal(msr) + if err != nil { + sublog.Error().Err(err).Msg("error marshalling propfind response xml") + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol") + w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8") + w.WriteHeader(http.StatusMultiStatus) + if _, err := w.Write(propRes); err != nil { + sublog.Error().Err(err).Msg("error writing propfind response") + return + } +} + +func hasProp(pf *propfind.XML, key string) bool { + for i := range pf.Prop { + k := fmt.Sprintf("%s/%s", pf.Prop[i].Space, pf.Prop[i].Local) + if k == key { + return true + } + } + return false +} diff --git a/internal/http/services/owncloud/ocdav/net/net.go b/internal/http/services/owncloud/ocdav/net/net.go index 9fab05c963..7ad433ab74 100644 --- a/internal/http/services/owncloud/ocdav/net/net.go +++ b/internal/http/services/owncloud/ocdav/net/net.go @@ -50,6 +50,8 @@ const ( PropQuotaUnknown = "-2" // PropOcFavorite is the favorite ns property PropOcFavorite = "http://owncloud.org/ns/favorite" + // PropOcMetaPathForUser is the meta-path-for-user ns property + PropOcMetaPathForUser = "http://owncloud.org/ns/meta-path-for-user" // DepthZero represents the webdav zero depth value DepthZero Depth = "0"