Skip to content

Commit

Permalink
Allow access to recycle bin for paths outside user homes (#2165)
Browse files Browse the repository at this point in the history
  • Loading branch information
ishank011 authored Oct 18, 2021
1 parent d78daa1 commit 284ce0f
Show file tree
Hide file tree
Showing 21 changed files with 205 additions and 163 deletions.
3 changes: 3 additions & 0 deletions changelog/unreleased/recycle-bin-arbitrary-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Enhancement: Allow access to recycle bin for arbitrary paths outside homes

https://github.com/cs3org/reva/pull/2165
3 changes: 1 addition & 2 deletions cmd/reva/recycle-list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"fmt"
"io"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
)
Expand All @@ -45,7 +44,7 @@ func recycleListCommand() *command {
return err
}

req := &gateway.ListRecycleRequest{
req := &provider.ListRecycleRequest{
Ref: &provider.Reference{
Path: getHomeRes.Path,
},
Expand Down
3 changes: 1 addition & 2 deletions cmd/reva/recycle-purge.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package main
import (
"io"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
)
Expand All @@ -44,7 +43,7 @@ func recyclePurgeCommand() *command {
return err
}

req := &gateway.PurgeRecycleRequest{
req := &provider.PurgeRecycleRequest{
Ref: &provider.Reference{
Path: getHomeRes.Path,
},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/cheggaaa/pb v1.0.29
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e
github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11
github.com/cs3org/go-cs3apis v0.0.0-20211018122138-391b29bd7803
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59
github.com/gdexlab/go-render v1.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11 h1:cc/8fdzWdr/wAZOXb29J8bnXjo1poCMCLwhlFBlvhfI=
github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/go-cs3apis v0.0.0-20211018122138-391b29bd7803 h1:R/6llgTNKxQQ7GaSTgFn6Fp8N50wIlagmdR7WY5LntM=
github.com/cs3org/go-cs3apis v0.0.0-20211018122138-391b29bd7803/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
18 changes: 5 additions & 13 deletions internal/grpc/services/gateway/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2065,25 +2065,20 @@ func (s *svc) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileV
return res, nil
}

func (s *svc) ListRecycleStream(_ *gateway.ListRecycleStreamRequest, _ gateway.GatewayAPI_ListRecycleStreamServer) error {
func (s *svc) ListRecycleStream(_ *provider.ListRecycleStreamRequest, _ gateway.GatewayAPI_ListRecycleStreamServer) error {
return errtypes.NotSupported("ListRecycleStream unimplemented")
}

// TODO use the ListRecycleRequest.Ref to only list the trash of a specific storage
func (s *svc) ListRecycle(ctx context.Context, req *gateway.ListRecycleRequest) (*provider.ListRecycleResponse, error) {
func (s *svc) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) {
c, err := s.find(ctx, req.GetRef())
if err != nil {
return &provider.ListRecycleResponse{
Status: status.NewStatusFromErrType(ctx, "ListFileVersions ref="+req.Ref.String(), err),
}, nil
}

res, err := c.ListRecycle(ctx, &provider.ListRecycleRequest{
Opaque: req.Opaque,
FromTs: req.FromTs,
ToTs: req.ToTs,
Ref: req.Ref,
})
res, err := c.ListRecycle(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "gateway: error calling ListRecycleRequest")
}
Expand All @@ -2107,18 +2102,15 @@ func (s *svc) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecyc
return res, nil
}

func (s *svc) PurgeRecycle(ctx context.Context, req *gateway.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
func (s *svc) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
c, err := s.find(ctx, req.Ref)
if err != nil {
return &provider.PurgeRecycleResponse{
Status: status.NewStatusFromErrType(ctx, "PurgeRecycle ref="+req.Ref.String(), err),
}, nil
}

res, err := c.PurgeRecycle(ctx, &provider.PurgeRecycleRequest{
Opaque: req.GetOpaque(),
Ref: req.GetRef(),
})
res, err := c.PurgeRecycle(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "gateway: error calling PurgeRecycle")
}
Expand Down
19 changes: 13 additions & 6 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,8 @@ func (s *service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss p
return err
}

items, err := s.storage.ListRecycle(ctx, ref.ResourceId.OpaqueId, ref.Path)
key, itemPath := router.ShiftPath(req.Key)
items, err := s.storage.ListRecycle(ctx, ref.GetPath(), key, itemPath)
if err != nil {
var st *rpc.Status
switch err.(type) {
Expand Down Expand Up @@ -866,8 +867,8 @@ func (s *service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequ
if err != nil {
return nil, err
}
key, itemPath := router.ShiftPath(ref.Path)
items, err := s.storage.ListRecycle(ctx, key, itemPath)
key, itemPath := router.ShiftPath(req.Key)
items, err := s.storage.ListRecycle(ctx, ref.GetPath(), key, itemPath)
// TODO(labkode): CRITICAL: fill recycle info with storage provider.
if err != nil {
var st *rpc.Status
Expand Down Expand Up @@ -897,7 +898,8 @@ func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreR
if err != nil {
return nil, err
}
if err := s.storage.RestoreRecycleItem(ctx, req.Key, ref.Path, req.RestoreRef); err != nil {
key, itemPath := router.ShiftPath(req.Key)
if err := s.storage.RestoreRecycleItem(ctx, ref.GetPath(), key, itemPath, req.RestoreRef); err != nil {
var st *rpc.Status
switch err.(type) {
case errtypes.IsNotFound:
Expand All @@ -919,9 +921,14 @@ func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreR
}

func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
ref, err := s.unwrap(ctx, req.Ref)
if err != nil {
return nil, err
}
// if a key was sent as opaque id purge only that item
if req.GetRef() != nil && req.GetRef().GetResourceId() != nil && req.GetRef().GetResourceId().OpaqueId != "" {
if err := s.storage.PurgeRecycleItem(ctx, req.GetRef().GetResourceId().OpaqueId, req.GetRef().Path); err != nil {
key, itemPath := router.ShiftPath(req.Key)
if key != "" {
if err := s.storage.PurgeRecycleItem(ctx, ref.GetPath(), key, itemPath); err != nil {
var st *rpc.Status
switch err.(type) {
case errtypes.IsNotFound:
Expand Down
114 changes: 44 additions & 70 deletions internal/http/services/owncloud/ocdav/trashbin.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (

rtrace "github.com/cs3org/reva/pkg/trace"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
Expand Down Expand Up @@ -98,18 +97,39 @@ func (h *TrashbinHandler) Handler(s *svc) http.Handler {
return
}

// key will be a base63 encoded cs3 path, it uniquely identifies a trash item & storage
// key will be a base64 encoded cs3 path, it uniquely identifies a trash item & storage
var key string
key, r.URL.Path = router.ShiftPath(r.URL.Path)

// TODO another options handler should not be necessary
// if r.Method == http.MethodOptions {
// s.doOptions(w, r, "trashbin")
// return
//}
// If the recycle bin corresponding to a speicific path is requested, use that.
// If not, we user the user home to route the request
basePath := r.URL.Query().Get("base_path")
if basePath == "" {
gc, err := pool.GetGatewayServiceClient(s.c.GatewaySvc)
if err != nil {
// TODO(jfd) how do we make the user aware that some storages are not available?
// opaque response property? Or a list of errors?
// add a recycle entry with the path to the storage that produced the error?
log.Error().Err(err).Msg("error getting gateway client")
w.WriteHeader(http.StatusInternalServerError)
return
}

getHomeRes, err := gc.GetHome(ctx, &provider.GetHomeRequest{})
if err != nil {
log.Error().Err(err).Msg("error calling GetHome")
w.WriteHeader(http.StatusInternalServerError)
return
}
if getHomeRes.Status.Code != rpc.Code_CODE_OK {
HandleErrorStatus(log, w, getHomeRes.Status)
return
}
basePath = getHomeRes.Path
}

if r.Method == MethodPropfind {
h.listTrashbin(w, r, s, u, key, r.URL.Path)
h.listTrashbin(w, r, s, u, basePath, key, r.URL.Path)
return
}
if key != "" && r.Method == MethodMove {
Expand All @@ -129,20 +149,20 @@ func (h *TrashbinHandler) Handler(s *svc) http.Handler {

log.Debug().Str("key", key).Str("dst", dst).Msg("restore")

h.restore(w, r, s, u, dst, key, r.URL.Path)
h.restore(w, r, s, u, basePath, dst, key, r.URL.Path)
return
}

if r.Method == http.MethodDelete {
h.delete(w, r, s, u, key, r.URL.Path)
h.delete(w, r, s, u, basePath, key, r.URL.Path)
return
}

http.Error(w, "501 Not implemented", http.StatusNotImplemented)
})
}

func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, key, itemPath string) {
func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, basePath, key, itemPath string) {
ctx, span := rtrace.Provider.Tracer("trash-bin").Start(r.Context(), "list_trashbin")
defer span.End()

Expand Down Expand Up @@ -195,19 +215,8 @@ func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s
return
}

getHomeRes, err := gc.GetHome(ctx, &provider.GetHomeRequest{})
if err != nil {
sublog.Error().Err(err).Msg("error calling GetHome")
w.WriteHeader(http.StatusInternalServerError)
return
}
if getHomeRes.Status.Code != rpc.Code_CODE_OK {
HandleErrorStatus(&sublog, w, getHomeRes.Status)
return
}

// ask gateway for recycle items
getRecycleRes, err := gc.ListRecycle(ctx, &gateway.ListRecycleRequest{Ref: &provider.Reference{Path: path.Join(getHomeRes.Path, key, itemPath)}})
getRecycleRes, err := gc.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: &provider.Reference{Path: basePath}, Key: path.Join(key, itemPath)})

if err != nil {
sublog.Error().Err(err).Msg("error calling ListRecycle")
Expand Down Expand Up @@ -235,7 +244,7 @@ func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s

for len(stack) > 0 {
key := stack[len(stack)-1]
getRecycleRes, err := gc.ListRecycle(ctx, &gateway.ListRecycleRequest{Ref: &provider.Reference{Path: path.Join(getHomeRes.Path, key)}})
getRecycleRes, err := gc.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: &provider.Reference{Path: basePath}, Key: key})
if err != nil {
sublog.Error().Err(err).Msg("error calling ListRecycle")
w.WriteHeader(http.StatusInternalServerError)
Expand Down Expand Up @@ -430,7 +439,7 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, u *use
return &response, nil
}

func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, dst, key, itemPath string) {
func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, basePath, dst, key, itemPath string) {
ctx, span := rtrace.Provider.Tracer("trash-bin").Start(r.Context(), "restore")
defer span.End()

Expand All @@ -455,19 +464,8 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc
return
}

getHomeRes, err := client.GetHome(ctx, &provider.GetHomeRequest{})
if err != nil {
sublog.Error().Err(err).Msg("error calling GetHome")
w.WriteHeader(http.StatusInternalServerError)
return
}
if getHomeRes.Status.Code != rpc.Code_CODE_OK {
HandleErrorStatus(&sublog, w, getHomeRes.Status)
return
}

dstRef := &provider.Reference{
Path: path.Join(getHomeRes.Path, dst),
Path: path.Join(basePath, dst),
}

dstStatReq := &provider.StatRequest{
Expand All @@ -490,7 +488,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc
// restore location exists, and if it doesn't returns a conflict error code.
if dstStatRes.Status.Code == rpc.Code_CODE_NOT_FOUND && isNested(dst) {
parentStatReq := &provider.StatRequest{
Ref: &provider.Reference{Path: path.Join(getHomeRes.Path, filepath.Dir(dst))},
Ref: &provider.Reference{Path: path.Join(basePath, filepath.Dir(dst))},
}

parentStatResponse, err := client.Stat(ctx, parentStatReq)
Expand Down Expand Up @@ -542,9 +540,9 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc
// use the key which is prefixed with the StoragePath to lookup the correct storage ...
// TODO currently limited to the home storage
Ref: &provider.Reference{
Path: path.Join(getHomeRes.Path, itemPath),
Path: basePath,
},
Key: key,
Key: path.Join(key, itemPath),
RestoreRef: &provider.Reference{Path: dst},
}

Expand Down Expand Up @@ -589,7 +587,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc
}

// delete has only a key
func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, key, itemPath string) {
func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, u *userpb.User, basePath, key, itemPath string) {
ctx, span := rtrace.Provider.Tracer("trash-bin").Start(r.Context(), "erase")
defer span.End()

Expand All @@ -602,38 +600,14 @@ func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc,
return
}

getHomeRes, err := client.GetHome(ctx, &provider.GetHomeRequest{})
if err != nil {
sublog.Error().Err(err).Msg("error calling GetHomeProvider")
w.WriteHeader(http.StatusInternalServerError)
return
}
if getHomeRes.Status.Code != rpc.Code_CODE_OK {
HandleErrorStatus(&sublog, w, getHomeRes.Status)
return
}
sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{Path: getHomeRes.Path}})
if err != nil {
sublog.Error().Err(err).Msg("error calling Stat")
w.WriteHeader(http.StatusInternalServerError)
return
}
if sRes.Status.Code != rpc.Code_CODE_OK {
HandleErrorStatus(&sublog, w, sRes.Status)
return
}

// set key as opaque id, the storageprovider will use it as the key for the
// storage drives PurgeRecycleItem key call

req := &gateway.PurgeRecycleRequest{
req := &provider.PurgeRecycleRequest{
Ref: &provider.Reference{
ResourceId: &provider.ResourceId{
StorageId: sRes.Info.Id.StorageId,
OpaqueId: key,
},
Path: utils.MakeRelativePath(itemPath),
Path: basePath,
},
Key: path.Join(key, itemPath),
}

res, err := client.PurgeRecycle(ctx, req)
Expand All @@ -646,9 +620,9 @@ func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc,
case rpc.Code_CODE_OK:
w.WriteHeader(http.StatusNoContent)
case rpc.Code_CODE_NOT_FOUND:
sublog.Debug().Str("storageid", sRes.Info.Id.StorageId).Str("key", key).Interface("status", res.Status).Msg("resource not found")
sublog.Debug().Str("path", basePath).Str("key", key).Interface("status", res.Status).Msg("resource not found")
w.WriteHeader(http.StatusConflict)
m := fmt.Sprintf("storageid %v not found", sRes.Info.Id.StorageId)
m := fmt.Sprintf("path %s not found", basePath)
b, err := Marshal(exception{
code: SabredavConflict,
message: m,
Expand Down
Loading

0 comments on commit 284ce0f

Please sign in to comment.