adjust REPORT to PROPFIND endpoint
Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
Bugfix: Fix search report
|
||||||
|
|
||||||
|
There were multiple issues with REPORT search responses from webdav. Also we want it to be consistent with PROPFIND responses.
|
||||||
|
* the `remote.php` prefix was missing from the href (added even though not neccessary)
|
||||||
|
* the ids were formatted wrong, they should look different for shares and spaces.
|
||||||
|
* the name of the resource was missing
|
||||||
|
* the shareid was missing (for shares)
|
||||||
|
* the prop `shareroot` (containing the name of the share root) was missing
|
||||||
|
* the permissions prop was empty
|
||||||
|
|
||||||
|
https://github.com/owncloud/web/issues/7557
|
||||||
|
https://github.com/owncloud/ocis/pull/4484
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -28,6 +29,25 @@ import (
|
|||||||
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Permissions is copied from reva internal conversion pkg
|
||||||
|
type Permissions uint
|
||||||
|
|
||||||
|
// consts are copied from reva internal conversion pkg
|
||||||
|
const (
|
||||||
|
// PermissionInvalid represents an invalid permission
|
||||||
|
PermissionInvalid Permissions = 0
|
||||||
|
// PermissionRead grants read permissions on a resource
|
||||||
|
PermissionRead Permissions = 1 << (iota - 1)
|
||||||
|
// PermissionWrite grants write permissions on a resource
|
||||||
|
PermissionWrite
|
||||||
|
// PermissionCreate grants create permissions on a resource
|
||||||
|
PermissionCreate
|
||||||
|
// PermissionDelete grants delete permissions on a resource
|
||||||
|
PermissionDelete
|
||||||
|
// PermissionShare grants share permissions on a resource
|
||||||
|
PermissionShare
|
||||||
|
)
|
||||||
|
|
||||||
var ListenEvents = []events.Unmarshaller{
|
var ListenEvents = []events.Unmarshaller{
|
||||||
events.ItemTrashed{},
|
events.ItemTrashed{},
|
||||||
events.ItemRestored{},
|
events.ItemRestored{},
|
||||||
@@ -119,7 +139,11 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
|
|||||||
SpaceId: space.Root.SpaceId,
|
SpaceId: space.Root.SpaceId,
|
||||||
OpaqueId: space.Root.OpaqueId,
|
OpaqueId: space.Root.OpaqueId,
|
||||||
}
|
}
|
||||||
var mountpointRootId *searchmsg.ResourceID
|
var (
|
||||||
|
mountpointRootID *searchmsg.ResourceID
|
||||||
|
rootName string
|
||||||
|
permissions *provider.ResourcePermissions
|
||||||
|
)
|
||||||
mountpointPrefix := ""
|
mountpointPrefix := ""
|
||||||
switch space.SpaceType {
|
switch space.SpaceType {
|
||||||
case "mountpoint":
|
case "mountpoint":
|
||||||
@@ -127,7 +151,7 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
|
|||||||
case "grant":
|
case "grant":
|
||||||
// In case of grant spaces we search the root of the outer space and translate the paths to the according mountpoint
|
// In case of grant spaces we search the root of the outer space and translate the paths to the according mountpoint
|
||||||
searchRootId.OpaqueId = space.Root.SpaceId
|
searchRootId.OpaqueId = space.Root.SpaceId
|
||||||
mountpointId, ok := mountpointMap[space.Id.OpaqueId]
|
mountpointID, ok := mountpointMap[space.Id.OpaqueId]
|
||||||
if !ok {
|
if !ok {
|
||||||
p.logger.Warn().Interface("space", space).Msg("could not find mountpoint space for grant space")
|
p.logger.Warn().Interface("space", space).Msg("could not find mountpoint space for grant space")
|
||||||
continue
|
continue
|
||||||
@@ -144,17 +168,19 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mountpointPrefix = utils.MakeRelativePath(gpRes.Path)
|
mountpointPrefix = utils.MakeRelativePath(gpRes.Path)
|
||||||
sid, spid, oid, err := storagespace.SplitID(mountpointId)
|
sid, spid, oid, err := storagespace.SplitID(mountpointID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Str("mountpointId", mountpointId).Msg("invalid mountpoint space id")
|
p.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Str("mountpointId", mountpointID).Msg("invalid mountpoint space id")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mountpointRootId = &searchmsg.ResourceID{
|
mountpointRootID = &searchmsg.ResourceID{
|
||||||
StorageId: sid,
|
StorageId: sid,
|
||||||
SpaceId: spid,
|
SpaceId: spid,
|
||||||
OpaqueId: oid,
|
OpaqueId: oid,
|
||||||
}
|
}
|
||||||
p.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootId).Msg("searching a grant")
|
rootName = space.GetRootInfo().GetPath()
|
||||||
|
permissions = space.GetRootInfo().GetPermissionSet()
|
||||||
|
p.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootID).Msg("searching a grant")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := p.indexClient.Search(ctx, &searchsvc.SearchIndexRequest{
|
res, err := p.indexClient.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||||
@@ -176,9 +202,11 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
|
|||||||
if mountpointPrefix != "" {
|
if mountpointPrefix != "" {
|
||||||
match.Entity.Ref.Path = utils.MakeRelativePath(strings.TrimPrefix(match.Entity.Ref.Path, mountpointPrefix))
|
match.Entity.Ref.Path = utils.MakeRelativePath(strings.TrimPrefix(match.Entity.Ref.Path, mountpointPrefix))
|
||||||
}
|
}
|
||||||
if mountpointRootId != nil {
|
if mountpointRootID != nil {
|
||||||
match.Entity.Ref.ResourceId = mountpointRootId
|
match.Entity.Ref.ResourceId = mountpointRootID
|
||||||
}
|
}
|
||||||
|
match.Entity.ShareRootName = rootName
|
||||||
|
match.Entity.Permissions = convertToOCS(permissions)
|
||||||
matches = append(matches, match)
|
matches = append(matches, match)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,3 +307,40 @@ func formatQuery(q string) string {
|
|||||||
// this is a basic filename search
|
// this is a basic filename search
|
||||||
return "Name:*" + strings.ReplaceAll(strings.ToLower(query), " ", `\ `) + "*"
|
return "Name:*" + strings.ReplaceAll(strings.ToLower(query), " ", `\ `) + "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: this converts cs3 to ocs permissions
|
||||||
|
// since conversions pkg is reva internal we have no other choice than to duplicate the logic
|
||||||
|
func convertToOCS(p *provider.ResourcePermissions) string {
|
||||||
|
var ocs Permissions
|
||||||
|
if p == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if p.ListContainer &&
|
||||||
|
p.ListFileVersions &&
|
||||||
|
p.ListRecycle &&
|
||||||
|
p.Stat &&
|
||||||
|
p.GetPath &&
|
||||||
|
p.GetQuota &&
|
||||||
|
p.InitiateFileDownload {
|
||||||
|
ocs |= PermissionRead
|
||||||
|
}
|
||||||
|
if p.InitiateFileUpload &&
|
||||||
|
p.RestoreFileVersion &&
|
||||||
|
p.RestoreRecycleItem {
|
||||||
|
ocs |= PermissionWrite
|
||||||
|
}
|
||||||
|
if p.ListContainer &&
|
||||||
|
p.Stat &&
|
||||||
|
p.CreateContainer &&
|
||||||
|
p.InitiateFileUpload {
|
||||||
|
ocs |= PermissionCreate
|
||||||
|
}
|
||||||
|
if p.Delete &&
|
||||||
|
p.PurgeRecycle {
|
||||||
|
ocs |= PermissionDelete
|
||||||
|
}
|
||||||
|
if p.AddGrant {
|
||||||
|
ocs |= PermissionShare
|
||||||
|
}
|
||||||
|
return strconv.FormatUint(uint64(ocs), 10)
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
|
|
||||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/utils"
|
||||||
searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
|
searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
|
||||||
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
||||||
"github.com/owncloud/ocis/v2/services/webdav/pkg/net"
|
"github.com/owncloud/ocis/v2/services/webdav/pkg/net"
|
||||||
@@ -102,8 +104,39 @@ func multistatusResponse(ctx context.Context, matches []*searchmsg.Match) ([]byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind.ResponseXML, error) {
|
func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind.ResponseXML, error) {
|
||||||
|
// unfortunately search uses own versions of ResourceId and Ref. So we need to assert them here
|
||||||
|
var (
|
||||||
|
ref string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// to copy PROPFIND behaviour we need to deliver different ids
|
||||||
|
// for shares it needs to be sharestorageproviderid!shareid
|
||||||
|
// for other spaces it needs to be storageproviderid$spaceid
|
||||||
|
switch match.Entity.Ref.ResourceId.StorageId {
|
||||||
|
default:
|
||||||
|
ref, err = storagespace.FormatReference(&provider.Reference{
|
||||||
|
ResourceId: &provider.ResourceId{
|
||||||
|
StorageId: match.Entity.Ref.ResourceId.StorageId,
|
||||||
|
SpaceId: match.Entity.Ref.ResourceId.SpaceId,
|
||||||
|
},
|
||||||
|
Path: match.Entity.Ref.Path,
|
||||||
|
})
|
||||||
|
case utils.ShareStorageProviderID:
|
||||||
|
ref, err = storagespace.FormatReference(&provider.Reference{
|
||||||
|
ResourceId: &provider.ResourceId{
|
||||||
|
//StorageId: match.Entity.Ref.ResourceId.StorageId,
|
||||||
|
SpaceId: match.Entity.Ref.ResourceId.SpaceId,
|
||||||
|
OpaqueId: match.Entity.Ref.ResourceId.OpaqueId,
|
||||||
|
},
|
||||||
|
Path: match.Entity.Ref.Path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
response := propfind.ResponseXML{
|
response := propfind.ResponseXML{
|
||||||
Href: net.EncodePath(path.Join("/dav/spaces/", match.Entity.Ref.ResourceId.StorageId+"!"+match.Entity.Ref.ResourceId.OpaqueId, match.Entity.Ref.Path)),
|
Href: net.EncodePath(path.Join("/remote.php/dav/spaces/", ref)),
|
||||||
Propstat: []propfind.PropstatXML{},
|
Propstat: []propfind.PropstatXML{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,10 +145,22 @@ func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind
|
|||||||
Prop: []prop.PropertyXML{},
|
Prop: []prop.PropertyXML{},
|
||||||
}
|
}
|
||||||
|
|
||||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:fileid", match.Entity.Id.StorageId+"!"+match.Entity.Id.OpaqueId))
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:fileid", storagespace.FormatResourceID(provider.ResourceId{
|
||||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", match.Entity.Etag))
|
StorageId: match.Entity.Id.StorageId,
|
||||||
|
SpaceId: match.Entity.Id.SpaceId,
|
||||||
|
OpaqueId: match.Entity.Id.OpaqueId,
|
||||||
|
})))
|
||||||
|
if match.Entity.Ref.ResourceId.StorageId == utils.ShareStorageProviderID {
|
||||||
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:shareid", match.Entity.Ref.ResourceId.OpaqueId))
|
||||||
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:shareroot", match.Entity.ShareRootName))
|
||||||
|
}
|
||||||
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:name", match.Entity.Name))
|
||||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", match.Entity.LastModifiedTime.AsTime().Format(time.RFC3339)))
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", match.Entity.LastModifiedTime.AsTime().Format(time.RFC3339)))
|
||||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
|
||||||
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", match.Entity.Permissions))
|
||||||
|
|
||||||
|
// those seem empty - bug?
|
||||||
|
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", match.Entity.Etag))
|
||||||
|
|
||||||
size := strconv.FormatUint(match.Entity.Size, 10)
|
size := strconv.FormatUint(match.Entity.Size, 10)
|
||||||
if match.Entity.Type == uint64(provider.ResourceType_RESOURCE_TYPE_CONTAINER) {
|
if match.Entity.Type == uint64(provider.ResourceType_RESOURCE_TYPE_CONTAINER) {
|
||||||
|
|||||||
Reference in New Issue
Block a user