From 5cefb2bec7d164ddb25af38dc4ffbe0a2fdc8cdd Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 2 Dec 2020 15:37:02 +0100 Subject: [PATCH 1/2] return share-types in ocs propfind responses Implements https://github.com/owncloud/ocis/issues/929 --- changelog/unreleased/ocis-ocs-share-types.md | 6 ++++ .../grpc/services/gateway/storageprovider.go | 5 ++-- .../http/services/owncloud/ocdav/propfind.go | 21 ++++++++++---- pkg/storage/fs/ocis/node.go | 29 ++++++++++++++++++- pkg/storage/fs/ocis/ocis.go | 4 +-- 5 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 changelog/unreleased/ocis-ocs-share-types.md diff --git a/changelog/unreleased/ocis-ocs-share-types.md b/changelog/unreleased/ocis-ocs-share-types.md new file mode 100644 index 0000000000..c185458b64 --- /dev/null +++ b/changelog/unreleased/ocis-ocs-share-types.md @@ -0,0 +1,6 @@ +Enhancement: include share types in ocs propfind responses + +Added the share types to the ocs propfind response when a resource has been shared. + +https://github.com/owncloud/ocis/issues/929 +https://github.com/cs3org/reva/pull/1329 diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index a563201268..3987b1aedd 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -1249,13 +1249,14 @@ func (s *svc) ListContainerStream(_ *provider.ListContainerStreamRequest, _ gate return errtypes.NotSupported("Unimplemented") } -func (s *svc) listHome(ctx context.Context) (*provider.ListContainerResponse, error) { +func (s *svc) listHome(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { lcr, err := s.listContainer(ctx, &provider.ListContainerRequest{ Ref: &provider.Reference{ Spec: &provider.Reference_Path{ Path: s.getHome(ctx), }, }, + ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, }) if err != nil { return &provider.ListContainerResponse{ @@ -1361,7 +1362,7 @@ func (s *svc) ListContainer(ctx context.Context, req *provider.ListContainerRequ } if path.Clean(p) == s.getHome(ctx) { - return s.listHome(ctx) + return s.listHome(ctx, req) } if s.isSharedFolder(ctx, p) { diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go index 57fecfebf4..ca781e40c0 100644 --- a/internal/http/services/owncloud/ocdav/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind.go @@ -108,6 +108,9 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) if info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER && depth == "1" { req := &provider.ListContainerRequest{ Ref: ref, + ArbitraryMetadataKeys: []string{ + "http://owncloud.org/ns/share-types", + }, } res, err := client.ListContainer(ctx, req) if err != nil { @@ -143,6 +146,9 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) } req := &provider.ListContainerRequest{ Ref: ref, + ArbitraryMetadataKeys: []string{ + "http://owncloud.org/ns/share-types", + }, } res, err := client.ListContainer(ctx, req) if err != nil { @@ -284,7 +290,6 @@ func (s *svc) newProp(key, val string) *propertyXML { // ns is the CS3 namespace that needs to be removed from the CS3 path before // prefixing it with the baseURI func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provider.ResourceInfo, ns string) (*responseXML, error) { - md.Path = strings.TrimPrefix(md.Path, ns) baseURI := ctx.Value(ctxKeyBaseURI).(string) @@ -442,6 +447,15 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide } else { propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:checksums", "")) } + case "share-types": // desktop + k := md.GetArbitraryMetadata() + amd := k.GetMetadata() + if amdv, ok := amd[fmt.Sprintf("%s/%s", pf.Prop[i].Space, pf.Prop[i].Local)]; ok { + st := fmt.Sprintf("%s", amdv) + propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:share-types", st)) + } else { + propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:"+pf.Prop[i].Local, "")) + } case "owner-display-name": // phoenix only // TODO(jfd): lookup displayname? or let clients do that? They should cache that IMO fallthrough @@ -459,11 +473,6 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide // see https://doc.owncloud.com/server/admin_manual/configuration/server/occ_command.html#maintenance-commands // TODO(jfd): double check the client behavior with reva on backup restore fallthrough - case "share-types": // desktop - // - // 1 - // - fallthrough default: propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:"+pf.Prop[i].Local, "")) } diff --git a/pkg/storage/fs/ocis/node.go b/pkg/storage/fs/ocis/node.go index bcde7b28cc..fd24f7771d 100644 --- a/pkg/storage/fs/ocis/node.go +++ b/pkg/storage/fs/ocis/node.go @@ -34,12 +34,18 @@ import ( "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/mime" + "github.com/cs3org/reva/pkg/sdk/common" "github.com/cs3org/reva/pkg/storage/utils/ace" "github.com/pkg/errors" "github.com/pkg/xattr" "github.com/rs/zerolog/log" ) +const ( + _ShareTypesKey = "http://owncloud.org/ns/share-types" + _UserShareType = "0" +) + // Node represents a node in the tree and provides methods to get a Parent or Child instance type Node struct { lu *Lookup @@ -291,7 +297,7 @@ func (n *Node) Owner() (id string, idp string, err error) { } // AsResourceInfo return the node as CS3 ResourceInfo -func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, err error) { +func (n *Node) AsResourceInfo(ctx context.Context, mdKeys []string) (ri *provider.ResourceInfo, err error) { log := appctx.GetLogger(ctx) var fn string @@ -399,6 +405,12 @@ func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, e log.Error().Err(err).Interface("node", n).Msg("could not list attributes") } + if common.FindString(mdKeys, _ShareTypesKey) != -1 { + if n.hasUserShares(ctx) { + ri.ArbitraryMetadata.Metadata[_ShareTypesKey] = _UserShareType + } + } + log.Debug(). Interface("ri", ri). Msg("AsResourceInfo") @@ -469,3 +481,18 @@ func (n *Node) ReadGrant(ctx context.Context, grantee string) (g *provider.Grant } return e.Grant(), nil } + +func (n *Node) hasUserShares(ctx context.Context) bool { + g, err := n.ListGrantees(ctx) + if err != nil { + appctx.GetLogger(ctx).Error().Err(err).Msg("hasUserShares: listGrantees") + return false + } + + for i := range g { + if strings.Contains(g[i], grantPrefix+"u") { + return true + } + } + return false +} diff --git a/pkg/storage/fs/ocis/ocis.go b/pkg/storage/fs/ocis/ocis.go index 59f092997b..2cb9d2be10 100644 --- a/pkg/storage/fs/ocis/ocis.go +++ b/pkg/storage/fs/ocis/ocis.go @@ -371,7 +371,7 @@ func (fs *ocisfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []s return nil, errtypes.PermissionDenied(node.ID) } - return node.AsResourceInfo(ctx) + return node.AsResourceInfo(ctx, mdKeys) } func (fs *ocisfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) (finfos []*provider.ResourceInfo, err error) { @@ -402,7 +402,7 @@ func (fs *ocisfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKey } for i := range children { - if ri, err := children[i].AsResourceInfo(ctx); err == nil { + if ri, err := children[i].AsResourceInfo(ctx, mdKeys); err == nil { finfos = append(finfos, ri) } } From 3cf1dc7dffd3288132886c7a2493d5aea9f72463 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 7 Dec 2020 15:57:47 +0100 Subject: [PATCH 2/2] add constants for ace prefixes --- pkg/storage/fs/ocis/grants.go | 4 ++-- pkg/storage/fs/ocis/node.go | 10 +++++----- pkg/storage/fs/ocis/permissions.go | 11 ++++++++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/pkg/storage/fs/ocis/grants.go b/pkg/storage/fs/ocis/grants.go index a2a86de354..ae5f3f5364 100644 --- a/pkg/storage/fs/ocis/grants.go +++ b/pkg/storage/fs/ocis/grants.go @@ -124,9 +124,9 @@ func (fs *ocisfs) RemoveGrant(ctx context.Context, ref *provider.Reference, g *p var attr string if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP { - attr = grantPrefix + "g:" + g.Grantee.Id.OpaqueId + attr = grantPrefix + _groupAcePrefix + g.Grantee.Id.OpaqueId } else { - attr = grantPrefix + "u:" + g.Grantee.Id.OpaqueId + attr = grantPrefix + _userAcePrefix + g.Grantee.Id.OpaqueId } np := fs.lu.toInternalPath(node.ID) diff --git a/pkg/storage/fs/ocis/node.go b/pkg/storage/fs/ocis/node.go index fd24f7771d..d8547aff7f 100644 --- a/pkg/storage/fs/ocis/node.go +++ b/pkg/storage/fs/ocis/node.go @@ -42,8 +42,8 @@ import ( ) const ( - _ShareTypesKey = "http://owncloud.org/ns/share-types" - _UserShareType = "0" + _shareTypesKey = "http://owncloud.org/ns/share-types" + _userShareType = "0" ) // Node represents a node in the tree and provides methods to get a Parent or Child instance @@ -405,9 +405,9 @@ func (n *Node) AsResourceInfo(ctx context.Context, mdKeys []string) (ri *provide log.Error().Err(err).Interface("node", n).Msg("could not list attributes") } - if common.FindString(mdKeys, _ShareTypesKey) != -1 { + if common.FindString(mdKeys, _shareTypesKey) != -1 { if n.hasUserShares(ctx) { - ri.ArbitraryMetadata.Metadata[_ShareTypesKey] = _UserShareType + ri.ArbitraryMetadata.Metadata[_shareTypesKey] = _userShareType } } @@ -490,7 +490,7 @@ func (n *Node) hasUserShares(ctx context.Context) bool { } for i := range g { - if strings.Contains(g[i], grantPrefix+"u") { + if strings.Contains(g[i], grantPrefix+_userAcePrefix) { return true } } diff --git a/pkg/storage/fs/ocis/permissions.go b/pkg/storage/fs/ocis/permissions.go index 70d7b96dc7..d74c0f4f31 100644 --- a/pkg/storage/fs/ocis/permissions.go +++ b/pkg/storage/fs/ocis/permissions.go @@ -31,6 +31,11 @@ import ( "github.com/pkg/xattr" ) +const ( + _userAcePrefix = "u:" + _groupAcePrefix = "g:" +) + var defaultPermissions *provider.ResourcePermissions = &provider.ResourcePermissions{ // no permissions } @@ -100,15 +105,15 @@ func (p *Permissions) HasPermission(ctx context.Context, n *Node, check func(*pr return false, err } - userace := grantPrefix + "u:" + u.Id.OpaqueId + userace := grantPrefix + _userAcePrefix + u.Id.OpaqueId userFound := false for i := range grantees { // we only need the find the user once per node switch { case !userFound && grantees[i] == userace: g, err = cn.ReadGrant(ctx, grantees[i]) - case strings.HasPrefix(grantees[i], grantPrefix+"g:"): - gr := strings.TrimPrefix(grantees[i], grantPrefix+"g:") + case strings.HasPrefix(grantees[i], grantPrefix+_groupAcePrefix): + gr := strings.TrimPrefix(grantees[i], grantPrefix+_groupAcePrefix) if groupsMap[gr] { g, err = cn.ReadGrant(ctx, grantees[i]) } else {