bump reva

Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
jkoberg
2023-05-24 17:39:20 +02:00
parent 924f0c408d
commit 9beb6090eb
11 changed files with 219 additions and 168 deletions

View File

@@ -241,6 +241,11 @@ func (s *svc) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSp
// TODO check for allowed filters
filters["mask"] = mask
}
path := utils.ReadPlainFromOpaque(req.Opaque, "path")
if path != "" {
// TODO check for allowed filters
filters["path"] = path
}
for _, f := range req.Filters {
switch f.Type {

View File

@@ -53,6 +53,7 @@ import (
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"golang.org/x/sync/errgroup"
"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
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))
for i := range mds {
res, err := mdToPropResponse(ctx, pf, mds[i], publicURL, ns, linkshares, returnMinimal)
if err != nil {
return nil, err
g, ctx := errgroup.WithContext(ctx)
type work struct {
position int
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()
@@ -904,11 +959,12 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
defer span.End()
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)
ref := path.Join(baseURI, md.Path)
ref := path.Join(baseURI, p)
if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
ref += "/"
}
@@ -952,7 +1008,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
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
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
if ls != nil && md.Id != nil && md.Id.SpaceId == utils.PublicStorageSpaceID && md.Id.OpaqueId == ls.Token {
md.Id = ls.ResourceId
if ls != nil && id != nil && id.SpaceId == utils.PublicStorageSpaceID && id.OpaqueId == ls.Token {
id = ls.ResourceId
}
propstatOK := PropstatXML{
@@ -996,12 +1052,12 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
if pf.Allprop != nil {
// return all known properties
if md.Id != nil {
id := storagespace.FormatResourceID(*md.Id)
if id != nil {
sid := storagespace.FormatResourceID(*id)
appendToOK(
prop.Escaped("oc:id", id),
prop.Escaped("oc:fileid", id),
prop.Escaped("oc:spaceid", md.Id.SpaceId),
prop.Escaped("oc:id", sid),
prop.Escaped("oc:fileid", sid),
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
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()))
}
@@ -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?
// I tested the desktop client and phoenix to annotate which properties are requestted, see below cases
case "fileid": // phoenix only
if md.Id != nil {
appendToOK(prop.Escaped("oc:fileid", storagespace.FormatResourceID(*md.Id)))
if id != nil {
appendToOK(prop.Escaped("oc:fileid", storagespace.FormatResourceID(*id)))
} else {
appendToNotFound(prop.NotFound("oc:fileid"))
}
case "id": // desktop client only
if md.Id != nil {
appendToOK(prop.Escaped("oc:id", storagespace.FormatResourceID(*md.Id)))
if id != nil {
appendToOK(prop.Escaped("oc:id", storagespace.FormatResourceID(*id)))
} else {
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"))
}
case "spaceid":
if md.Id != nil {
appendToOK(prop.Escaped("oc:spaceid", md.Id.SpaceId))
if id != nil {
appendToOK(prop.Escaped("oc:spaceid", id.SpaceId))
} else {
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)?
// 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
if ls == nil || strings.HasPrefix(md.Path, "/"+ls.Token+"/") {
if ls == nil || strings.HasPrefix(p, "/"+ls.Token+"/") {
appendToOK(prop.Escaped("oc:size", size))
} else {
// 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 _, ok := linkshares[md.Id.OpaqueId]; ok {
if id != nil {
if _, ok := linkshares[id.OpaqueId]; ok {
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 {
var path string
if !ls.PasswordProtected {
path = md.Path
path = p
} else {
expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos))
var sb strings.Builder
sb.WriteString(md.Path)
sb.WriteString(p)
sb.WriteString("?signature=")
sb.WriteString(ls.Signature.Signature)
sb.WriteString("&expiration=")
@@ -1335,8 +1391,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
}
case "privatelink":
privateURL, err := url.Parse(publicURL)
if err == nil && md.Id != nil {
privateURL.Path = path.Join(privateURL.Path, "f", storagespace.FormatResourceID(*md.Id))
if err == nil && id != nil {
privateURL.Path = path.Join(privateURL.Path, "f", storagespace.FormatResourceID(*id))
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:privatelink", privateURL.String()))
} else {
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":
if isPublic {
// 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))
var sb strings.Builder
sb.WriteString("<oc:signature>")
@@ -1366,7 +1422,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
case "name":
appendToOK(prop.Escaped("oc:name", md.Name))
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()))
}
case "dDC": // desktop

View File

@@ -273,7 +273,7 @@ func (h *Handler) listPublicShares(r *http.Request, filters []*link.ListPublicSh
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())
client, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil {
@@ -291,19 +291,19 @@ func (h *Handler) isPublicShare(r *http.Request, oid string) bool {
})
if err != nil {
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{}
logger := appctx.GetLogger(r.Context())
gwC, err := pool.GetGatewayServiceClient(h.gatewayAddr)
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)
return
}
@@ -317,21 +317,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
return
}
before, err := gwC.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
}
createdByUser := publicshare.IsCreatedByUser(*before.Share, user)
createdByUser := publicshare.IsCreatedByUser(*share, user)
// 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 {
@@ -355,9 +341,9 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
}
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 {
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)
return
}
@@ -382,7 +368,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
newName, ok := r.Form["name"]
if ok {
updatesFound = true
if newName[0] != before.Share.DisplayName {
if newName[0] != share.DisplayName {
updates = append(updates, &link.UpdatePublicShareRequest_Update{
Type: link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME,
DisplayName: newName[0],
@@ -404,7 +390,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
publicSharePermissions := &link.PublicSharePermissions{
Permissions: newPermissions,
}
beforePerm, _ := json.Marshal(before.GetShare().Permissions)
beforePerm, _ := json.Marshal(share.Permissions)
afterPerm, _ := json.Marshal(publicSharePermissions)
if 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)
if err != nil {
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)
if string(afterExpiration) != string(beforeExpiration) {
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
if len(updates) > 0 {
uRes := &link.UpdatePublicShareResponse{Share: before.Share}
uRes := &link.UpdatePublicShareResponse{Share: share}
for k := range updates {
uRes, err = gwC.UpdatePublicShare(r.Context(), &link.UpdatePublicShareRequest{
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: shareID,
OpaqueId: share.Id.OpaqueId,
},
},
},
Update: updates[k],
})
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)
return
}
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)
return
}
}
publicShare = uRes.Share
share = uRes.Share
} else if !updatesFound {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "No updates specified in request", nil)
return
}
s := conversions.PublicShare2ShareData(publicShare, r, h.publicURL)
s := conversions.PublicShare2ShareData(share, r, h.publicURL)
h.addFileInfo(r.Context(), s, statRes.Info)
h.mapUserIds(r.Context(), gwC, 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()
c, err := pool.GetGatewayServiceClient(h.gatewayAddr)
@@ -522,25 +506,11 @@ func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, shar
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)
if !publicshare.IsCreatedByUser(*before.Share, u) {
sRes, err := c.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: before.Share.ResourceId}})
if !publicshare.IsCreatedByUser(*share, u) {
sRes, err := c.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: share.ResourceId}})
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)
return
}
@@ -555,7 +525,7 @@ func (h *Handler) removePublicShare(w http.ResponseWriter, r *http.Request, shar
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: shareID,
OpaqueId: share.GetId().GetOpaqueId(),
},
},
},

View File

@@ -679,16 +679,21 @@ func (h *Handler) UpdateShare(w http.ResponseWriter, r *http.Request) {
shareID := chi.URLParam(r, "shareid")
// FIXME: isPublicShare is already doing a GetShare and GetPublicShare,
// we should just reuse that object when doing updates
if h.isPublicShare(r, shareID) {
h.updatePublicShare(w, r, shareID)
if share, ok := h.isPublicShare(r, shareID); ok {
h.updatePublicShare(w, r, share)
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()
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)
if err != nil {
@@ -696,28 +701,7 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return
}
shareR, err := client.GetShare(r.Context(), &collaboration.GetShareRequest{
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)
info, status, err := h.getResourceInfoByID(ctx, client, share.ResourceId)
if err != nil || status.Code != rpc.Code_CODE_OK {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
return
@@ -729,7 +713,7 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return
}
shareR.Share.Permissions = &collaboration.SharePermissions{Permissions: role.CS3ResourcePermissions()}
share.Permissions = &collaboration.SharePermissions{Permissions: role.CS3ResourcePermissions()}
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)),
}
shareR.Share.Expiration = expirationTs
share.Expiration = expirationTs
fieldMaskPaths = append(fieldMaskPaths, "expiration")
} else if r.Form.Has("expireDate") {
// 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")
}
uReq := &collaboration.UpdateShareRequest{
Share: shareR.Share,
Share: share,
UpdateMask: &fieldmaskpb.FieldMask{
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 {
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 {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error mapping share data", err)
return
@@ -807,24 +791,30 @@ func (h *Handler) updateShare(w http.ResponseWriter, r *http.Request, shareID st
return
}
h.addFileInfo(r.Context(), share, statRes.Info)
h.mapUserIds(ctx, client, share)
h.addFileInfo(r.Context(), resultshare, statRes.Info)
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)
func (h *Handler) RemoveShare(w http.ResponseWriter, r *http.Request) {
shareID := chi.URLParam(r, "shareid")
switch {
case h.isPublicShare(r, shareID):
h.removePublicShare(w, r, shareID)
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.isPublicShare(r, shareID); ok {
h.removePublicShare(w, r, share)
return
}
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

View File

@@ -143,7 +143,21 @@ func (h *Handler) addSpaceMember(w http.ResponseWriter, r *http.Request, info *p
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()
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
}
p, err := h.findProvider(ctx, &ref)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
return
}
providerClient, err := h.getStorageProviderClient(p)
providerClient, err := h.getStorageProviderClient(prov)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider client", err)
return

View File

@@ -129,7 +129,7 @@ func (h *Handler) createUserShare(w http.ResponseWriter, r *http.Request, statIn
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())
client, err := pool.GetGatewayServiceClient(h.gatewayAddr)
if err != nil {
@@ -147,13 +147,13 @@ func (h *Handler) isUserShare(r *http.Request, oid string) bool {
})
if err != nil {
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()
uClient, err := h.getClient()
@@ -164,26 +164,11 @@ func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, shareI
shareRef := &collaboration.ShareReference{
Spec: &collaboration.ShareReference_Id{
Id: &collaboration.ShareId{
OpaqueId: shareID,
},
Id: share.Id,
},
}
// 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 {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "deleting share failed", err)
return
@@ -207,7 +192,7 @@ func (h *Handler) removeUserShare(w http.ResponseWriter, r *http.Request, shareI
return
}
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)
}

View File

@@ -22,6 +22,7 @@ import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
"regexp"
"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
// 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)
pathSegments := strings.Split(strings.TrimPrefix(requestedPath, string(os.PathSeparator)), string(os.PathSeparator))
deepestMountPath := ""
var deepestMountSpace *providerpb.StorageSpace
var deepestMountPathProvider *registrypb.ProviderInfo
@@ -549,12 +551,42 @@ func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, pa
var spaces []*providerpb.StorageSpace
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
filters := []*providerpb.ListStorageSpacesRequest_Filter{
{
Type: providerpb.ListStorageSpacesRequest_Filter_TYPE_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
switch {
case spacePath == path && unique:
case spacePath == requestedPath && unique:
validSpaces = append(validSpaces, space)
deepestMountPath = spacePath
deepestMountSpace = space
deepestMountPathProvider = p
case !unique && isSubpath(spacePath, path):
case !unique && isSubpath(spacePath, requestedPath):
// and add all providers below and exactly matching the path
// requested /foo, mountPath /foo/sub
validSpaces = append(validSpaces, space)
@@ -612,7 +644,7 @@ func (r *registry) findProvidersForAbsolutePathReference(ctx context.Context, pa
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
// requested /foo/sub/mob
deepestMountPath = spacePath