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/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 bcde7b28cc..d8547aff7f 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+_userAcePrefix) {
+ 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)
}
}
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 {