bump reva

Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
jkoberg
2023-05-25 15:52:01 +02:00
parent 924f0c408d
commit 9beb6090eb
11 changed files with 219 additions and 168 deletions
+5
View File
@@ -0,0 +1,5 @@
Enhancement: Bump Reva
bumps reva
https://github.com/owncloud/ocis/pull/6381
+1 -1
View File
@@ -13,7 +13,7 @@ require (
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.6.0 github.com/coreos/go-oidc/v3 v3.6.0
github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d
github.com/cs3org/reva/v2 v2.13.4-0.20230523132457-4fb99464ab8d github.com/cs3org/reva/v2 v2.13.4-0.20230525110514-9334e2174492
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/egirna/icap-client v0.1.1 github.com/egirna/icap-client v0.1.1
+2 -2
View File
@@ -627,8 +627,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc=
github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
github.com/cs3org/reva/v2 v2.13.4-0.20230523132457-4fb99464ab8d h1:Wuz6lxPY022NP9052zcDFpnqhLkDtik11fYHX1NLF74= github.com/cs3org/reva/v2 v2.13.4-0.20230525110514-9334e2174492 h1:hWJK5O45XN73GVOvRreymiF0lbrHgMcIP4oFkGfuQcg=
github.com/cs3org/reva/v2 v2.13.4-0.20230523132457-4fb99464ab8d/go.mod h1:jkngU/36YdFNQfFurjgKl8URY2dkhwgJ5gQXt1GUoPw= github.com/cs3org/reva/v2 v2.13.4-0.20230525110514-9334e2174492/go.mod h1:jkngU/36YdFNQfFurjgKl8URY2dkhwgJ5gQXt1GUoPw=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
@@ -241,6 +241,11 @@ func (s *svc) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSp
// TODO check for allowed filters // TODO check for allowed filters
filters["mask"] = mask filters["mask"] = mask
} }
path := utils.ReadPlainFromOpaque(req.Opaque, "path")
if path != "" {
// TODO check for allowed filters
filters["path"] = path
}
for _, f := range req.Filters { for _, f := range req.Filters {
switch f.Type { switch f.Type {
@@ -53,6 +53,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/codes"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/types/known/fieldmaskpb" "google.golang.org/protobuf/types/known/fieldmaskpb"
) )
@@ -876,13 +877,67 @@ func ReadPropfind(r io.Reader) (pf XML, status int, err error) {
// MultistatusResponse converts a list of resource infos into a multistatus response string // MultistatusResponse converts a list of resource infos into a multistatus response string
func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool) ([]byte, error) { func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool) ([]byte, error) {
responses := make([]*ResponseXML, 0, len(mds)) g, ctx := errgroup.WithContext(ctx)
for i := range mds {
res, err := mdToPropResponse(ctx, pf, mds[i], publicURL, ns, linkshares, returnMinimal) type work struct {
if err != nil { position int
return nil, err info *provider.ResourceInfo
}
type result struct {
position int
info *ResponseXML
}
workChan := make(chan work, len(mds))
resultChan := make(chan result, len(mds))
// Distribute work
g.Go(func() error {
defer close(workChan)
for i, md := range mds {
select {
case workChan <- work{position: i, info: md}:
case <-ctx.Done():
return ctx.Err()
}
} }
responses = append(responses, res) return nil
})
// Spawn workers that'll concurrently work the queue
numWorkers := 50
if len(mds) < numWorkers {
numWorkers = len(mds)
}
for i := 0; i < numWorkers; i++ {
g.Go(func() error {
for work := range workChan {
res, err := mdToPropResponse(ctx, pf, work.info, publicURL, ns, linkshares, returnMinimal)
if err != nil {
return err
}
select {
case resultChan <- result{position: work.position, info: res}:
case <-ctx.Done():
return ctx.Err()
}
}
return nil
})
}
// Wait for things to settle down, then close results chan
go func() {
_ = g.Wait() // error is checked later
close(resultChan)
}()
if err := g.Wait(); err != nil {
return nil, err
}
responses := make([]*ResponseXML, len(mds))
for res := range resultChan {
responses[res.position] = res.info
} }
msr := NewMultiStatusResponseXML() msr := NewMultiStatusResponseXML()
@@ -904,11 +959,12 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
defer span.End() defer span.End()
sublog := appctx.GetLogger(ctx).With().Interface("md", md).Str("ns", ns).Logger() sublog := appctx.GetLogger(ctx).With().Interface("md", md).Str("ns", ns).Logger()
md.Path = strings.TrimPrefix(md.Path, ns) id := md.Id
p := strings.TrimPrefix(md.Path, ns)
baseURI := ctx.Value(net.CtxKeyBaseURI).(string) baseURI := ctx.Value(net.CtxKeyBaseURI).(string)
ref := path.Join(baseURI, md.Path) ref := path.Join(baseURI, p)
if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
ref += "/" ref += "/"
} }
@@ -952,7 +1008,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
role := conversions.RoleFromResourcePermissions(md.PermissionSet, ls != nil) role := conversions.RoleFromResourcePermissions(md.PermissionSet, ls != nil)
if md.Space != nil && md.Space.SpaceType != "grant" && utils.ResourceIDEqual(md.Space.Root, md.Id) { if md.Space != nil && md.Space.SpaceType != "grant" && utils.ResourceIDEqual(md.Space.Root, id) {
// a space root is never shared // a space root is never shared
shareTypes = "" shareTypes = ""
} }
@@ -969,8 +1025,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
} }
// replace fileid of /public/{token} mountpoint with grant fileid // replace fileid of /public/{token} mountpoint with grant fileid
if ls != nil && md.Id != nil && md.Id.SpaceId == utils.PublicStorageSpaceID && md.Id.OpaqueId == ls.Token { if ls != nil && id != nil && id.SpaceId == utils.PublicStorageSpaceID && id.OpaqueId == ls.Token {
md.Id = ls.ResourceId id = ls.ResourceId
} }
propstatOK := PropstatXML{ propstatOK := PropstatXML{
@@ -996,12 +1052,12 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
if pf.Allprop != nil { if pf.Allprop != nil {
// return all known properties // return all known properties
if md.Id != nil { if id != nil {
id := storagespace.FormatResourceID(*md.Id) sid := storagespace.FormatResourceID(*id)
appendToOK( appendToOK(
prop.Escaped("oc:id", id), prop.Escaped("oc:id", sid),
prop.Escaped("oc:fileid", id), prop.Escaped("oc:fileid", sid),
prop.Escaped("oc:spaceid", md.Id.SpaceId), prop.Escaped("oc:spaceid", id.SpaceId),
) )
} }
@@ -1012,7 +1068,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
} }
// we need to add the shareid if possible - the only way to extract it here is to parse it from the path // we need to add the shareid if possible - the only way to extract it here is to parse it from the path
if ref, err := storagespace.ParseReference(strings.TrimPrefix(md.Path, "/")); err == nil && ref.GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID { if ref, err := storagespace.ParseReference(strings.TrimPrefix(p, "/")); err == nil && ref.GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID {
appendToOK(prop.Raw("oc:shareid", ref.GetResourceId().GetOpaqueId())) appendToOK(prop.Raw("oc:shareid", ref.GetResourceId().GetOpaqueId()))
} }
@@ -1119,14 +1175,14 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
// TODO(jfd): maybe phoenix and the other clients can just use this id as an opaque string? // TODO(jfd): maybe phoenix and the other clients can just use this id as an opaque string?
// I tested the desktop client and phoenix to annotate which properties are requestted, see below cases // I tested the desktop client and phoenix to annotate which properties are requestted, see below cases
case "fileid": // phoenix only case "fileid": // phoenix only
if md.Id != nil { if id != nil {
appendToOK(prop.Escaped("oc:fileid", storagespace.FormatResourceID(*md.Id))) appendToOK(prop.Escaped("oc:fileid", storagespace.FormatResourceID(*id)))
} else { } else {
appendToNotFound(prop.NotFound("oc:fileid")) appendToNotFound(prop.NotFound("oc:fileid"))
} }
case "id": // desktop client only case "id": // desktop client only
if md.Id != nil { if id != nil {
appendToOK(prop.Escaped("oc:id", storagespace.FormatResourceID(*md.Id))) appendToOK(prop.Escaped("oc:id", storagespace.FormatResourceID(*id)))
} else { } else {
appendToNotFound(prop.NotFound("oc:id")) appendToNotFound(prop.NotFound("oc:id"))
} }
@@ -1137,8 +1193,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
appendToNotFound(prop.NotFound("oc:file-parent")) appendToNotFound(prop.NotFound("oc:file-parent"))
} }
case "spaceid": case "spaceid":
if md.Id != nil { if id != nil {
appendToOK(prop.Escaped("oc:spaceid", md.Id.SpaceId)) appendToOK(prop.Escaped("oc:spaceid", id.SpaceId))
} else { } else {
appendToNotFound(prop.Escaped("oc:spaceid", "")) appendToNotFound(prop.Escaped("oc:spaceid", ""))
} }
@@ -1204,7 +1260,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
// TODO what is the difference to d:quota-used-bytes (which only exists for collections)? // TODO what is the difference to d:quota-used-bytes (which only exists for collections)?
// oc:size is available on files and folders and behaves like d:getcontentlength or d:quota-used-bytes respectively // oc:size is available on files and folders and behaves like d:getcontentlength or d:quota-used-bytes respectively
// The hasPrefix is a workaround to make children of the link root show a size if they have 0 bytes // The hasPrefix is a workaround to make children of the link root show a size if they have 0 bytes
if ls == nil || strings.HasPrefix(md.Path, "/"+ls.Token+"/") { if ls == nil || strings.HasPrefix(p, "/"+ls.Token+"/") {
appendToOK(prop.Escaped("oc:size", size)) appendToOK(prop.Escaped("oc:size", size))
} else { } else {
// link share root collection has no size // link share root collection has no size
@@ -1289,8 +1345,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
} }
} }
if md.Id != nil { if id != nil {
if _, ok := linkshares[md.Id.OpaqueId]; ok { if _, ok := linkshares[id.OpaqueId]; ok {
types.WriteString("<oc:share-type>3</oc:share-type>") types.WriteString("<oc:share-type>3</oc:share-type>")
} }
} }
@@ -1316,12 +1372,12 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
if isPublic && md.Type == provider.ResourceType_RESOURCE_TYPE_FILE { if isPublic && md.Type == provider.ResourceType_RESOURCE_TYPE_FILE {
var path string var path string
if !ls.PasswordProtected { if !ls.PasswordProtected {
path = md.Path path = p
} else { } else {
expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos)) expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos))
var sb strings.Builder var sb strings.Builder
sb.WriteString(md.Path) sb.WriteString(p)
sb.WriteString("?signature=") sb.WriteString("?signature=")
sb.WriteString(ls.Signature.Signature) sb.WriteString(ls.Signature.Signature)
sb.WriteString("&expiration=") sb.WriteString("&expiration=")
@@ -1335,8 +1391,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
} }
case "privatelink": case "privatelink":
privateURL, err := url.Parse(publicURL) privateURL, err := url.Parse(publicURL)
if err == nil && md.Id != nil { if err == nil && id != nil {
privateURL.Path = path.Join(privateURL.Path, "f", storagespace.FormatResourceID(*md.Id)) privateURL.Path = path.Join(privateURL.Path, "f", storagespace.FormatResourceID(*id))
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:privatelink", privateURL.String())) propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:privatelink", privateURL.String()))
} else { } else {
propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:privatelink")) propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:privatelink"))
@@ -1344,7 +1400,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
case "signature-auth": case "signature-auth":
if isPublic { if isPublic {
// We only want to add the attribute to the root of the propfind. // We only want to add the attribute to the root of the propfind.
if strings.HasSuffix(md.Path, ls.Token) && ls.Signature != nil { if strings.HasSuffix(p, ls.Token) && ls.Signature != nil {
expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos)) expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos))
var sb strings.Builder var sb strings.Builder
sb.WriteString("<oc:signature>") sb.WriteString("<oc:signature>")
@@ -1366,7 +1422,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
case "name": case "name":
appendToOK(prop.Escaped("oc:name", md.Name)) appendToOK(prop.Escaped("oc:name", md.Name))
case "shareid": case "shareid":
if ref, err := storagespace.ParseReference(strings.TrimPrefix(md.Path, "/")); err == nil && ref.GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID { if ref, err := storagespace.ParseReference(strings.TrimPrefix(p, "/")); err == nil && ref.GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID {
appendToOK(prop.Raw("oc:shareid", ref.GetResourceId().GetOpaqueId())) appendToOK(prop.Raw("oc:shareid", ref.GetResourceId().GetOpaqueId()))
} }
case "dDC": // desktop case "dDC": // desktop
@@ -273,7 +273,7 @@ func (h *Handler) listPublicShares(r *http.Request, filters []*link.ListPublicSh
return ocsDataPayload, nil, errors.New("bad request") return ocsDataPayload, nil, errors.New("bad request")
} }
func (h *Handler) isPublicShare(r *http.Request, oid string) bool { func (h *Handler) isPublicShare(r *http.Request, oid string) (*link.PublicShare, bool) {
logger := appctx.GetLogger(r.Context()) logger := appctx.GetLogger(r.Context())
client, err := pool.GetGatewayServiceClient(h.gatewayAddr) client, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil { if err != nil {
@@ -291,19 +291,19 @@ func (h *Handler) isPublicShare(r *http.Request, oid string) bool {
}) })
if err != nil { if err != nil {
logger.Err(err) logger.Err(err)
return false return nil, false
} }
return psRes.GetShare() != nil return psRes.GetShare(), psRes.GetShare() != nil
} }
func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shareID string) { func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, share *link.PublicShare) {
updates := []*link.UpdatePublicShareRequest_Update{} updates := []*link.UpdatePublicShareRequest_Update{}
logger := appctx.GetLogger(r.Context()) logger := appctx.GetLogger(r.Context())
gwC, err := pool.GetGatewayServiceClient(h.gatewayAddr) gwC, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil { if err != nil {
log.Err(err).Str("shareID", shareID).Msg("updatePublicShare") log.Err(err).Str("shareID", share.GetId().GetOpaqueId()).Msg("updatePublicShare")
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "error getting a connection to the gateway service", nil) response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "error getting a connection to the gateway service", nil)
return return
} }
@@ -317,21 +317,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
return return
} }
before, err := gwC.GetPublicShare(r.Context(), &link.GetPublicShareRequest{ createdByUser := publicshare.IsCreatedByUser(*share, user)
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: shareID,
},
},
},
})
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "failed to get public share", nil)
return
}
createdByUser := publicshare.IsCreatedByUser(*before.Share, user)
// NOTE: you are allowed to update a link TO a public link without the `PublicLink.Write` permission if you created it yourself // NOTE: you are allowed to update a link TO a public link without the `PublicLink.Write` permission if you created it yourself
if (permKey != nil && *permKey != 0) || !createdByUser { if (permKey != nil && *permKey != 0) || !createdByUser {
@@ -355,9 +341,9 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
} }
if !createdByUser { if !createdByUser {
sRes, err := gwC.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: before.Share.ResourceId}}) sRes, err := gwC.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: share.ResourceId}})
if err != nil { if err != nil {
log.Err(err).Interface("resource_id", before.Share.ResourceId).Msg("failed to stat shared resource") log.Err(err).Interface("resource_id", share.ResourceId).Msg("failed to stat shared resource")
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to get public share", nil) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to get public share", nil)
return return
} }
@@ -382,7 +368,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
newName, ok := r.Form["name"] newName, ok := r.Form["name"]
if ok { if ok {
updatesFound = true updatesFound = true
if newName[0] != before.Share.DisplayName { if newName[0] != share.DisplayName {
updates = append(updates, &link.UpdatePublicShareRequest_Update{ updates = append(updates, &link.UpdatePublicShareRequest_Update{
Type: link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME, Type: link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME,
DisplayName: newName[0], DisplayName: newName[0],
@@ -404,7 +390,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
publicSharePermissions := &link.PublicSharePermissions{ publicSharePermissions := &link.PublicSharePermissions{
Permissions: newPermissions, Permissions: newPermissions,
} }
beforePerm, _ := json.Marshal(before.GetShare().Permissions) beforePerm, _ := json.Marshal(share.Permissions)
afterPerm, _ := json.Marshal(publicSharePermissions) afterPerm, _ := json.Marshal(publicSharePermissions)
if string(beforePerm) != string(afterPerm) { if string(beforePerm) != string(afterPerm) {
logger.Info().Str("shares", "update").Msgf("updating permissions from %v to: %v", string(beforePerm), string(afterPerm)) logger.Info().Str("shares", "update").Msgf("updating permissions from %v to: %v", string(beforePerm), string(afterPerm))
@@ -418,7 +404,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
} }
} }
statReq := provider.StatRequest{Ref: &provider.Reference{ResourceId: before.Share.ResourceId}} statReq := provider.StatRequest{Ref: &provider.Reference{ResourceId: share.ResourceId}}
statRes, err := gwC.Stat(r.Context(), &statReq) statRes, err := gwC.Stat(r.Context(), &statReq)
if err != nil { if err != nil {
log.Debug().Err(err).Str("shares", "update public share").Msg("error during stat") log.Debug().Err(err).Str("shares", "update public share").Msg("error during stat")
@@ -446,7 +432,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
} }
} }
beforeExpiration, _ := json.Marshal(before.Share.Expiration) beforeExpiration, _ := json.Marshal(share.Expiration)
afterExpiration, _ := json.Marshal(newExpiration) afterExpiration, _ := json.Marshal(newExpiration)
if string(afterExpiration) != string(beforeExpiration) { if string(afterExpiration) != string(beforeExpiration) {
logger.Debug().Str("shares", "update").Msgf("updating expire date from %v to: %v", string(beforeExpiration), string(afterExpiration)) logger.Debug().Str("shares", "update").Msgf("updating expire date from %v to: %v", string(beforeExpiration), string(afterExpiration))
@@ -473,47 +459,45 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
}) })
} }
publicShare := before.Share
// Updates are atomical. See: https://github.com/cs3org/cs3apis/pull/67#issuecomment-617651428 so in order to get the latest updated version // Updates are atomical. See: https://github.com/cs3org/cs3apis/pull/67#issuecomment-617651428 so in order to get the latest updated version
if len(updates) > 0 { if len(updates) > 0 {
uRes := &link.UpdatePublicShareResponse{Share: before.Share} uRes := &link.UpdatePublicShareResponse{Share: share}
for k := range updates { for k := range updates {
uRes, err = gwC.UpdatePublicShare(r.Context(), &link.UpdatePublicShareRequest{ uRes, err = gwC.UpdatePublicShare(r.Context(), &link.UpdatePublicShareRequest{
Ref: &link.PublicShareReference{ Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{ Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{ Id: &link.PublicShareId{
OpaqueId: shareID, OpaqueId: share.Id.OpaqueId,
}, },
}, },
}, },
Update: updates[k], Update: updates[k],
}) })
if err != nil { if err != nil {
log.Err(err).Str("shareID", shareID).Msg("sending update request to public link provider") log.Err(err).Str("shareID", share.Id.OpaqueId).Msg("sending update request to public link provider")
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "Error sending update request to public link provider", err) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "Error sending update request to public link provider", err)
return return
} }
if uRes.Status.Code != rpc.Code_CODE_OK { if uRes.Status.Code != rpc.Code_CODE_OK {
log.Debug().Str("shareID", shareID).Msgf("sending update request to public link provider failed: %s", uRes.Status.Message) log.Debug().Str("shareID", share.Id.OpaqueId).Msgf("sending update request to public link provider failed: %s", uRes.Status.Message)
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, fmt.Sprintf("Error sending update request to public link provider: %s", uRes.Status.Message), nil) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, fmt.Sprintf("Error sending update request to public link provider: %s", uRes.Status.Message), nil)
return return
} }
} }
publicShare = uRes.Share share = uRes.Share
} else if !updatesFound { } else if !updatesFound {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "No updates specified in request", nil) response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "No updates specified in request", nil)
return return
} }
s := conversions.PublicShare2ShareData(publicShare, r, h.publicURL) s := conversions.PublicShare2ShareData(share, r, h.publicURL)
h.addFileInfo(r.Context(), s, statRes.Info) h.addFileInfo(r.Context(), s, statRes.Info)
h.mapUserIds(r.Context(), gwC, s) h.mapUserIds(r.Context(), gwC, s)
response.WriteOCSSuccess(w, r, s) response.WriteOCSSuccess(w, r, s)
} }
func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, shareID string) { func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, share *link.PublicShare) {
ctx := r.Context() ctx := r.Context()
c, err := pool.GetGatewayServiceClient(h.gatewayAddr) c, err := pool.GetGatewayServiceClient(h.gatewayAddr)
@@ -522,25 +506,11 @@ func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, shar
return return
} }
before, err := c.GetPublicShare(r.Context(), &link.GetPublicShareRequest{
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: shareID,
},
},
},
})
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "failed to get public share", nil)
return
}
u := ctxpkg.ContextMustGetUser(ctx) u := ctxpkg.ContextMustGetUser(ctx)
if !publicshare.IsCreatedByUser(*before.Share, u) { if !publicshare.IsCreatedByUser(*share, u) {
sRes, err := c.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: before.Share.ResourceId}}) sRes, err := c.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: share.ResourceId}})
if err != nil { if err != nil {
log.Err(err).Interface("resource_id", before.Share.ResourceId).Msg("failed to stat shared resource") log.Err(err).Interface("resource_id", share.ResourceId).Msg("failed to stat shared resource")
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to get public share", nil) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to get public share", nil)
return return
} }
@@ -555,7 +525,7 @@ func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, shar
Ref: &link.PublicShareReference{ Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{ Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{ Id: &link.PublicShareId{
OpaqueId: shareID, OpaqueId: share.GetId().GetOpaqueId(),
}, },
}, },
}, },
@@ -679,16 +679,21 @@ func (h *Handler) UpdateShare(w http.ResponseWriter, r *http.Request) {
shareID := chi.URLParam(r, "shareid") shareID := chi.URLParam(r, "shareid")
// FIXME: isPublicShare is already doing a GetShare and GetPublicShare, // FIXME: isPublicShare is already doing a GetShare and GetPublicShare,
// we should just reuse that object when doing updates // we should just reuse that object when doing updates
if h.isPublicShare(r, shareID) { if share, ok := h.isPublicShare(r, shareID); ok {
h.updatePublicShare(w, r, shareID) h.updatePublicShare(w, r, share)
return return
} }
h.updateShare(w, r, shareID) // TODO PUT is used with incomplete data to update a share}
if share, ok := h.isUserShare(r, shareID); ok {
h.updateShare(w, r, share) // TODO PUT is used with incomplete data to update a share}
return
}
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "cannot find share", nil)
} }
func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID string) { func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, share *collaboration.Share) {
ctx := r.Context() ctx := r.Context()
sublog := appctx.GetLogger(ctx).With().Str("shareID", shareID).Logger() sublog := appctx.GetLogger(ctx).With().Str("shareID", share.GetId().GetOpaqueId()).Logger()
client, err := pool.GetGatewayServiceClient(h.gatewayAddr) client, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil { if err != nil {
@@ -696,28 +701,7 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return return
} }
shareR, err := client.GetShare(r.Context(), &collaboration.GetShareRequest{ info, status, err := h.getResourceInfoByID(ctx, client, share.ResourceId)
Ref: &collaboration.ShareReference{
Spec: &collaboration.ShareReference_Id{
Id: &collaboration.ShareId{
OpaqueId: shareID,
},
},
},
})
if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc update share request", err)
return
}
if shareR.Status.GetCode() != rpc.Code_CODE_OK {
// TODO: error code from shareR response
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "cant find requested share", fmt.Errorf("Can't find share %s. Response code: %v", shareID, shareR.Status.GetCode()))
return
}
info, status, err := h.getResourceInfoByID(ctx, client, shareR.Share.ResourceId)
if err != nil || status.Code != rpc.Code_CODE_OK { if err != nil || status.Code != rpc.Code_CODE_OK {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
return return
@@ -729,7 +713,7 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return return
} }
shareR.Share.Permissions = &collaboration.SharePermissions{Permissions: role.CS3ResourcePermissions()} share.Permissions = &collaboration.SharePermissions{Permissions: role.CS3ResourcePermissions()}
var fieldMaskPaths = []string{"permissions"} var fieldMaskPaths = []string{"permissions"}
@@ -747,16 +731,16 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
Nanos: uint32(expiration.UnixNano() % int64(time.Second)), Nanos: uint32(expiration.UnixNano() % int64(time.Second)),
} }
shareR.Share.Expiration = expirationTs share.Expiration = expirationTs
fieldMaskPaths = append(fieldMaskPaths, "expiration") fieldMaskPaths = append(fieldMaskPaths, "expiration")
} else if r.Form.Has("expireDate") { } else if r.Form.Has("expireDate") {
// If the expiration parameter was sent but is empty, then the expiration should be removed. // If the expiration parameter was sent but is empty, then the expiration should be removed.
shareR.Share.Expiration = nil share.Expiration = nil
fieldMaskPaths = append(fieldMaskPaths, "expiration") fieldMaskPaths = append(fieldMaskPaths, "expiration")
} }
uReq := &collaboration.UpdateShareRequest{ uReq := &collaboration.UpdateShareRequest{
Share: shareR.Share, Share: share,
UpdateMask: &fieldmaskpb.FieldMask{ UpdateMask: &fieldmaskpb.FieldMask{
Paths: fieldMaskPaths, Paths: fieldMaskPaths,
}, },
@@ -777,10 +761,10 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
} }
if currentUser, ok := ctxpkg.ContextGetUser(ctx); ok { if currentUser, ok := ctxpkg.ContextGetUser(ctx); ok {
h.statCache.RemoveStat(currentUser.Id, shareR.Share.ResourceId) h.statCache.RemoveStat(currentUser.Id, share.ResourceId)
} }
share, err := conversions.CS3Share2ShareData(ctx, uRes.Share) resultshare, err := conversions.CS3Share2ShareData(ctx, uRes.Share)
if err != nil { if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
return return
@@ -807,24 +791,30 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return return
} }
h.addFileInfo(r.Context(), share, statRes.Info) h.addFileInfo(r.Context(), resultshare, statRes.Info)
h.mapUserIds(ctx, client, share) h.mapUserIds(ctx, client, resultshare)
response.WriteOCSSuccess(w, r, share) response.WriteOCSSuccess(w, r, resultshare)
} }
// RemoveShare handles DELETE requests on /apps/files_sharing/api/v1/shares/(shareid) // RemoveShare handles DELETE requests on /apps/files_sharing/api/v1/shares/(shareid)
func (h *Handler) RemoveShare(w http.ResponseWriter, r *http.Request) { func (h *Handler) RemoveShare(w http.ResponseWriter, r *http.Request) {
shareID := chi.URLParam(r, "shareid") shareID := chi.URLParam(r, "shareid")
switch { if share, ok := h.isPublicShare(r, shareID); ok {
case h.isPublicShare(r, shareID): h.removePublicShare(w, r, share)
h.removePublicShare(w, r, shareID) return
case h.isUserShare(r, shareID):
h.removeUserShare(w, r, shareID)
default:
// The request is a remove space member request.
h.removeSpaceMember(w, r, shareID)
} }
if share, ok := h.isUserShare(r, shareID); ok {
h.removeUserShare(w, r, share)
return
}
if prov, ok := h.isSpaceShare(r, shareID); ok {
// The request is a remove space member request.
h.removeSpaceMember(w, r, shareID, prov)
return
}
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "cannot find share", nil)
} }
// ListShares handles GET requests on /apps/files_sharing/api/v1/shares // ListShares handles GET requests on /apps/files_sharing/api/v1/shares
@@ -143,7 +143,21 @@ func (h *Handler) addSpaceMember(w http.ResponseWriter, r *http.Request, info *p
response.WriteOCSSuccess(w, r, nil) response.WriteOCSSuccess(w, r, nil)
} }
func (h *Handler) removeSpaceMember(w http.ResponseWriter, r *http.Request, spaceID string) { func (h *Handler) isSpaceShare(r *http.Request, spaceID string) (*registry.ProviderInfo, bool) {
ref, err := storagespace.ParseReference(spaceID)
if err != nil {
return nil, false
}
if ref.ResourceId.OpaqueId == "" {
ref.ResourceId.OpaqueId = ref.ResourceId.SpaceId
}
p, err := h.findProvider(r.Context(), &ref)
return p, err == nil
}
func (h *Handler) removeSpaceMember(w http.ResponseWriter, r *http.Request, spaceID string, prov *registry.ProviderInfo) {
ctx := r.Context() ctx := r.Context()
shareWith := r.URL.Query().Get("shareWith") shareWith := r.URL.Query().Get("shareWith")
@@ -168,13 +182,7 @@ func (h *Handler) removeSpaceMember(w http.ResponseWriter, r *http.Request, spac
ref.ResourceId.OpaqueId = ref.ResourceId.SpaceId ref.ResourceId.OpaqueId = ref.ResourceId.SpaceId
} }
p, err := h.findProvider(ctx, &ref) providerClient, err := h.getStorageProviderClient(prov)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
return
}
providerClient, err := h.getStorageProviderClient(p)
if err != nil { if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider client", err) response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider client", err)
return return
@@ -129,7 +129,7 @@ func (h *Handler) createUserShare(w http.ResponseWriter, r *http.Request, statIn
return share, nil return share, nil
} }
func (h *Handler) isUserShare(r *http.Request, oid string) bool { func (h *Handler) isUserShare(r *http.Request, oid string) (*collaboration.Share, bool) {
logger := appctx.GetLogger(r.Context()) logger := appctx.GetLogger(r.Context())
client, err := pool.GetGatewayServiceClient(h.gatewayAddr) client, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil { if err != nil {
@@ -147,13 +147,13 @@ func (h *Handler) isUserShare(r *http.Request, oid string) bool {
}) })
if err != nil { if err != nil {
logger.Err(err) logger.Err(err)
return false return nil, false
} }
return getShareRes.GetShare() != nil return getShareRes.GetShare(), getShareRes.GetShare() != nil
} }
func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, shareID string) { func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, share *collaboration.Share) {
ctx := r.Context() ctx := r.Context()
uClient, err := h.getClient() uClient, err := h.getClient()
@@ -164,26 +164,11 @@ func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, shareI
shareRef := &collaboration.ShareReference{ shareRef := &collaboration.ShareReference{
Spec: &collaboration.ShareReference_Id{ Spec: &collaboration.ShareReference_Id{
Id: &collaboration.ShareId{ Id: share.Id,
OpaqueId: shareID,
},
}, },
} }
// Get the share, so that we can include it in the response.
getShareResp, err := uClient.GetShare(ctx, &collaboration.GetShareRequest{Ref: shareRef})
if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error sending a grpc delete share request", err)
return
} else if getShareResp.Status.Code != rpc.Code_CODE_OK {
if getShareResp.Status.Code == rpc.Code_CODE_NOT_FOUND {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "not found", nil)
return
}
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err)
return
}
data, err := conversions.CS3Share2ShareData(ctx, getShareResp.Share) data, err := conversions.CS3Share2ShareData(ctx, share)
if err != nil { if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err) response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err)
return return
@@ -207,7 +192,7 @@ func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, shareI
return return
} }
if currentUser, ok := ctxpkg.ContextGetUser(ctx); ok { if currentUser, ok := ctxpkg.ContextGetUser(ctx); ok {
h.statCache.RemoveStat(currentUser.Id, getShareResp.Share.ResourceId) h.statCache.RemoveStat(currentUser.Id, share.ResourceId)
} }
response.WriteOCSSuccess(w, r, data) response.WriteOCSSuccess(w, r, data)
} }
+37 -5
View File
@@ -22,6 +22,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
@@ -534,9 +535,10 @@ func (r *registry) findProvidersForResource(ctx context.Context, id string, find
// findProvidersForAbsolutePathReference takes a path and returns the storage provider with the longest matching path prefix // findProvidersForAbsolutePathReference takes a path and returns the storage provider with the longest matching path prefix
// FIXME use regex to return the correct provider when multiple are configured // FIXME use regex to return the correct provider when multiple are configured
func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, path string, unique, unrestricted bool, _ string) []*registrypb.ProviderInfo { func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, requestedPath string, unique, unrestricted bool, _ string) []*registrypb.ProviderInfo {
currentUser := ctxpkg.ContextMustGetUser(ctx) currentUser := ctxpkg.ContextMustGetUser(ctx)
pathSegments := strings.Split(strings.TrimPrefix(requestedPath, string(os.PathSeparator)), string(os.PathSeparator))
deepestMountPath := "" deepestMountPath := ""
var deepestMountSpace *providerpb.StorageSpace var deepestMountSpace *providerpb.StorageSpace
var deepestMountPathProvider *registrypb.ProviderInfo var deepestMountPathProvider *registrypb.ProviderInfo
@@ -549,12 +551,42 @@ func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, pa
var spaces []*providerpb.StorageSpace var spaces []*providerpb.StorageSpace
var err error var err error
// check if any space in the provider has a valid mountpoint
containsRelatedSpace := false
spaceLoop:
for _, space := range provider.Spaces {
spacePath, _ := space.SpacePath(currentUser, nil)
spacePathSegments := strings.Split(strings.TrimPrefix(spacePath, string(os.PathSeparator)), string(os.PathSeparator))
for i, segment := range spacePathSegments {
if i >= len(pathSegments) {
break
}
if pathSegments[i] != segment {
if segment != "" && !strings.Contains(segment, "{{") {
// Mount path points elsewhere -> irrelevant
continue spaceLoop
}
// Encountered a template which couldn't be filled -> potentially relevant
break
}
}
containsRelatedSpace = true
break
}
if !containsRelatedSpace {
continue
}
// when listing paths also return mountpoints // when listing paths also return mountpoints
filters := []*providerpb.ListStorageSpacesRequest_Filter{ filters := []*providerpb.ListStorageSpacesRequest_Filter{
{ {
Type: providerpb.ListStorageSpacesRequest_Filter_TYPE_PATH, Type: providerpb.ListStorageSpacesRequest_Filter_TYPE_PATH,
Term: &providerpb.ListStorageSpacesRequest_Filter_Path{ Term: &providerpb.ListStorageSpacesRequest_Filter_Path{
Path: strings.TrimPrefix(path, p.ProviderPath), Path: strings.TrimPrefix(requestedPath, p.ProviderPath), // FIXME this no longer has an effect as the p.Providerpath is always empty
}, },
}, },
{ {
@@ -595,14 +627,14 @@ func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, pa
// determine deepest mount point // determine deepest mount point
switch { switch {
case spacePath == path && unique: case spacePath == requestedPath && unique:
validSpaces = append(validSpaces, space) validSpaces = append(validSpaces, space)
deepestMountPath = spacePath deepestMountPath = spacePath
deepestMountSpace = space deepestMountSpace = space
deepestMountPathProvider = p deepestMountPathProvider = p
case !unique && isSubpath(spacePath, path): case !unique && isSubpath(spacePath, requestedPath):
// and add all providers below and exactly matching the path // and add all providers below and exactly matching the path
// requested /foo, mountPath /foo/sub // requested /foo, mountPath /foo/sub
validSpaces = append(validSpaces, space) validSpaces = append(validSpaces, space)
@@ -612,7 +644,7 @@ func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, pa
deepestMountPathProvider = p deepestMountPathProvider = p
} }
case isSubpath(path, spacePath) && len(spacePath) > len(deepestMountPath): case isSubpath(requestedPath, spacePath) && len(spacePath) > len(deepestMountPath):
// eg. three providers: /foo, /foo/sub, /foo/sub/bar // eg. three providers: /foo, /foo/sub, /foo/sub/bar
// requested /foo/sub/mob // requested /foo/sub/mob
deepestMountPath = spacePath deepestMountPath = spacePath
+1 -1
View File
@@ -349,7 +349,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1
github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
github.com/cs3org/go-cs3apis/cs3/types/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1
# github.com/cs3org/reva/v2 v2.13.4-0.20230523132457-4fb99464ab8d # github.com/cs3org/reva/v2 v2.13.4-0.20230525110514-9334e2174492
## explicit; go 1.19 ## explicit; go 1.19
github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/internal/grace
github.com/cs3org/reva/v2/cmd/revad/runtime github.com/cs3org/reva/v2/cmd/revad/runtime