Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow access to recycle bin for paths outside user homes #2165

Merged
merged 8 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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