From 5ede25e0dc0e99b91c35b37d22d9e5a085870c68 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Fri, 15 Nov 2024 10:38:44 +0100 Subject: [PATCH] Partial revert of PR #4911 --- internal/grpc/services/gateway/gateway.go | 6 - .../grpc/services/gateway/storageprovider.go | 1178 ++++++++++++++++- .../storageprovider/storageprovider.go | 18 +- .../http/services/owncloud/ocdav/ocdav.go | 19 +- .../owncloud/ocs/data/capabilities.go | 2 +- .../handlers/cloud/capabilities/uploads.go | 59 +- 6 files changed, 1176 insertions(+), 106 deletions(-) diff --git a/internal/grpc/services/gateway/gateway.go b/internal/grpc/services/gateway/gateway.go index ebaf168985..20bea533e9 100644 --- a/internal/grpc/services/gateway/gateway.go +++ b/internal/grpc/services/gateway/gateway.go @@ -71,7 +71,6 @@ type config struct { EtagCacheTTL int `mapstructure:"etag_cache_ttl"` AllowedUserAgents map[string][]string `mapstructure:"allowed_user_agents"` // map[path][]user-agent CreateHomeCacheTTL int `mapstructure:"create_home_cache_ttl"` - HomeLayout string `mapstructure:"home_layout"` } // sets defaults. @@ -112,11 +111,6 @@ func (c *config) ApplyDefaults() { if c.TransferExpires == 0 { c.TransferExpires = 100 * 60 // seconds } - - // default to /home - if c.HomeLayout == "" { - c.HomeLayout = "/home" - } } type svc struct { diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index 3d51a9eb00..a9278a13c9 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -20,6 +20,7 @@ package gateway import ( "context" + "fmt" "net/url" "path" "strings" @@ -27,6 +28,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" @@ -35,13 +37,14 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" - "github.com/cs3org/reva/pkg/storage/utils/templates" + "github.com/cs3org/reva/pkg/storage/utils/etag" "github.com/cs3org/reva/pkg/utils" "github.com/golang-jwt/jwt" "github.com/google/uuid" "github.com/pkg/errors" "google.golang.org/grpc/codes" gstatus "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) // transferClaims are custom claims for a JWT token to be used between the metadata and data gateways. @@ -77,6 +80,7 @@ func (s *svc) sign(_ context.Context, target, versionKey string) (string, error) func (s *svc) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) { log := appctx.GetLogger(ctx) + home := s.getHome(ctx) c, err := s.findByPath(ctx, home) if err != nil { @@ -102,30 +106,171 @@ func (s *svc) GetHome(ctx context.Context, _ *provider.GetHomeRequest) (*provide }, nil } -func (s *svc) getHome(ctx context.Context) string { - u := appctx.ContextMustGetUser(ctx) - return templates.WithUser(u, s.c.HomeLayout) +func (s *svc) getHome(_ context.Context) string { + // TODO(labkode): issue #601, /home will be hardcoded. + return "/home" } func (s *svc) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*gateway.InitiateFileDownloadResponse, error) { + log := appctx.GetLogger(ctx) + if utils.IsRelativeReference(req.Ref) { return s.initiateFileDownload(ctx, req) } - statReq := &provider.StatRequest{Ref: req.Ref} - statRes, err := s.stat(ctx, statReq) - if err != nil { + p, st := s.getPath(ctx, req.Ref) + if st.Code != rpc.Code_CODE_OK { return &gateway.InitiateFileDownloadResponse{ - Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + Status: st, }, nil } - if statRes.Status.Code != rpc.Code_CODE_OK { + + if !s.inSharedFolder(ctx, p) { + statReq := &provider.StatRequest{Ref: req.Ref} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + if statRes.Status.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileDownloadResponse{ + Status: statRes.Status, + }, nil + } + return s.initiateFileDownload(ctx, req) + } + + if s.isSharedFolder(ctx, p) { + log.Debug().Str("path", p).Msg("path points to shared folder") + err := errtypes.PermissionDenied("gateway: cannot download share folder: path=" + p) + log.Err(err).Msg("gateway: error downloading") return &gateway.InitiateFileDownloadResponse{ - Status: statRes.Status, + Status: status.NewInvalidArg(ctx, "path points to share folder"), }, nil } - return s.initiateFileDownload(ctx, req) + if s.isShareName(ctx, p) { + statReq := &provider.StatRequest{Ref: req.Ref} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + if statRes.Status.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileDownloadResponse{ + Status: statRes.Status, + }, nil + } + + if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { + err := errtypes.BadRequest(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) + log.Err(err).Msg("gateway: error stating share name") + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error initiating download"), + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + // TODO(ishank011): pass this through the datagateway service + // For now, we just expose the file server to the user + ep, opaque, err := s.webdavRefTransferEndpoint(ctx, statRes.Info.Target) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error downloading from webdav host: "+p), + }, nil + } + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileDownloadProtocol{ + { + Opaque: opaque, + Protocol: "simple", + DownloadEndpoint: ep, + }, + }, + }, nil + } + + // if it is a file allow download + if ri.Type == provider.ResourceType_RESOURCE_TYPE_FILE { + log.Debug().Str("path", p).Interface("ri", ri).Msg("path points to share name file") + req.Ref.Path = ri.Path + log.Debug().Str("path", ri.Path).Msg("download") + return s.initiateFileDownload(ctx, req) + } + + log.Debug().Str("path", p).Interface("statRes", statRes).Msg("path:%s points to share name") + err = errtypes.PermissionDenied("gateway: cannot download share name: path=" + p) + log.Err(err).Str("path", p).Msg("gateway: error downloading") + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInvalidArg(ctx, "path points to share name"), + }, nil + } + + if s.isShareChild(ctx, p) { + log.Debug().Msgf("shared child: %s", p) + shareName, shareChild := s.splitShare(ctx, p) + + statReq := &provider.StatRequest{ + Ref: &provider.Reference{Path: shareName}, + } + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileDownloadResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + // TODO(ishank011): pass this through the datagateway service + // For now, we just expose the file server to the user + ep, opaque, err := s.webdavRefTransferEndpoint(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error downloading from webdav host: "+p), + }, nil + } + return &gateway.InitiateFileDownloadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileDownloadProtocol{ + { + Opaque: opaque, + Protocol: "simple", + DownloadEndpoint: ep, + }, + }, + }, nil + } + + // append child to target + req.Ref.Path = path.Join(ri.Path, shareChild) + log.Debug().Str("path", req.Ref.Path).Msg("download") + return s.initiateFileDownload(ctx, req) + } + + panic("gateway: download: unknown path:" + p) } func versionKey(req *provider.InitiateFileDownloadRequest) string { @@ -195,7 +340,148 @@ func (s *svc) initiateFileDownload(ctx context.Context, req *provider.InitiateFi } func (s *svc) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*gateway.InitiateFileUploadResponse, error) { - return s.initiateFileUpload(ctx, req) + log := appctx.GetLogger(ctx) + if utils.IsRelativeReference(req.Ref) { + return s.initiateFileUpload(ctx, req) + } + p, st := s.getPath(ctx, req.Ref) + if st.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileUploadResponse{ + Status: st, + }, nil + } + + if !s.inSharedFolder(ctx, p) { + return s.initiateFileUpload(ctx, req) + } + + if s.isSharedFolder(ctx, p) { + log.Debug().Str("path", p).Msg("path points to shared folder") + err := errtypes.PermissionDenied("gateway: cannot upload to share folder: path=" + p) + log.Err(err).Msg("gateway: error downloading") + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInvalidArg(ctx, "path points to share folder"), + }, nil + } + + if s.isShareName(ctx, p) { + log.Debug().Str("path", p).Msg("path points to share name") + statReq := &provider.StatRequest{Ref: req.Ref} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + if statRes.Status.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileUploadResponse{ + Status: statRes.Status, + }, nil + } + + if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { + err := errtypes.BadRequest(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) + log.Err(err).Msg("gateway: error stating share name") + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error initiating upload"), + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + // TODO(ishank011): pass this through the datagateway service + // For now, we just expose the file server to the user + ep, opaque, err := s.webdavRefTransferEndpoint(ctx, statRes.Info.Target) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error downloading from webdav host: "+p), + }, nil + } + return &gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileUploadProtocol{ + { + Opaque: opaque, + Protocol: "simple", + UploadEndpoint: ep, + }, + }, + }, nil + } + + // if it is a file allow upload + if ri.Type == provider.ResourceType_RESOURCE_TYPE_FILE { + log.Debug().Str("path", p).Interface("ri", ri).Msg("path points to share name file") + req.Ref.Path = ri.Path + log.Debug().Str("path", ri.Path).Msg("upload") + return s.initiateFileUpload(ctx, req) + } + + err = errtypes.PermissionDenied("gateway: cannot upload to share name: path=" + p) + log.Err(err).Msg("gateway: error uploading") + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInvalidArg(ctx, "path points to share name"), + }, nil + } + + if s.isShareChild(ctx, p) { + log.Debug().Msgf("shared child: %s", p) + shareName, shareChild := s.splitShare(ctx, p) + + statReq := &provider.StatRequest{Ref: &provider.Reference{Path: shareName}} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &gateway.InitiateFileUploadResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + // TODO(ishank011): pass this through the datagateway service + // For now, we just expose the file server to the user + ep, opaque, err := s.webdavRefTransferEndpoint(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, err, "gateway: error uploading to webdav host: "+p), + }, nil + } + return &gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileUploadProtocol{ + { + Opaque: opaque, + Protocol: "simple", + UploadEndpoint: ep, + }, + }, + }, nil + } + + // append child to target + req.Ref.Path = path.Join(ri.Path, shareChild) + return s.initiateFileUpload(ctx, req) + } + + panic("gateway: upload: unknown path:" + p) } func (s *svc) initiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*gateway.InitiateFileUploadResponse, error) { @@ -280,11 +566,75 @@ func (s *svc) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provi } func (s *svc) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) { + log := appctx.GetLogger(ctx) + if utils.IsRelativeReference(req.Ref) { return s.createContainer(ctx, req) } - return s.createContainer(ctx, req) + p, st := s.getPath(ctx, req.Ref) + if st.Code != rpc.Code_CODE_OK { + return &provider.CreateContainerResponse{ + Status: st, + }, nil + } + + if !s.inSharedFolder(ctx, p) { + return s.createContainer(ctx, req) + } + + if s.isSharedFolder(ctx, p) || s.isShareName(ctx, p) { + log.Debug().Msgf("path:%s points to shared folder or share name", p) + err := errtypes.PermissionDenied("gateway: cannot create container on share folder or share name: path=" + p) + log.Err(err).Msg("gateway: error creating container") + return &provider.CreateContainerResponse{ + Status: status.NewInvalidArg(ctx, "path points to share folder or share name"), + }, nil + } + + if s.isShareChild(ctx, p) { + log.Debug().Msgf("shared child: %s", p) + shareName, shareChild := s.splitShare(ctx, p) + + statReq := &provider.StatRequest{Ref: &provider.Reference{Path: shareName}} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &provider.CreateContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.CreateContainerResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &provider.CreateContainerResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + err = s.webdavRefMkdir(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &provider.CreateContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error creating container on webdav host: "+p), + }, nil + } + return &provider.CreateContainerResponse{ + Status: status.NewOK(ctx), + }, nil + } + + // append child to target + req.Ref.Path = path.Join(ri.Path, shareChild) + return s.createContainer(ctx, req) + } + + panic("gateway: create container on unknown path:" + p) } func (s *svc) createContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) { @@ -325,8 +675,137 @@ func (s *svc) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*p return res, nil } +// check if the path contains the prefix of the shared folder. +func (s *svc) inSharedFolder(ctx context.Context, p string) bool { + sharedFolder := s.getSharedFolder(ctx) + return strings.HasPrefix(p, sharedFolder) +} + func (s *svc) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { - return s.delete(ctx, req) + log := appctx.GetLogger(ctx) + p, st := s.getPath(ctx, req.Ref) + if st.Code != rpc.Code_CODE_OK { + return &provider.DeleteResponse{ + Status: st, + }, nil + } + + if !s.inSharedFolder(ctx, p) { + return s.delete(ctx, req) + } + + if s.isSharedFolder(ctx, p) { + // TODO(labkode): deleting share names should be allowed, means unmounting. + return &provider.DeleteResponse{ + Status: status.NewInvalidArg(ctx, "path points to share folder or share name"), + }, nil + } + + if s.isShareName(ctx, p) { + log.Debug().Msgf("path:%s points to share name", p) + + sRes, err := s.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{}) + if err != nil { + return nil, err + } + + statRes, err := s.Stat(ctx, &provider.StatRequest{ + Ref: &provider.Reference{ + Path: p, + }, + }) + if err != nil { + return nil, err + } + + // the following will check that: + // - the resource to delete is a share the current user received + // - signal the storage the delete must not land in the trashbin + // - delete the resource and update the share status to "rejected" + for _, share := range sRes.Shares { + if statRes != nil && (share.Share.ResourceId.OpaqueId == statRes.Info.Id.OpaqueId) && (share.Share.ResourceId.StorageId == statRes.Info.Id.StorageId) { + // this opaque needs explanation. It signals the storage the resource we're about to delete does not + // belong to the current user because it was share to her, thus delete the "node" and don't send it to + // the trash bin, since the share can be mounted as many times as desired. + req.Opaque = &types.Opaque{ + Map: map[string]*types.OpaqueEntry{ + "deleting_shared_resource": { + Value: []byte("true"), + Decoder: "plain", + }, + }, + } + + // the following block takes care of updating the state of the share to "rejected". This will ensure the user + // can "Accept" the share once again. + // TODO should this be pending? If so, update the two comments above as well. If not, get rid of this comment. + share.State = collaboration.ShareState_SHARE_STATE_REJECTED + r := &collaboration.UpdateReceivedShareRequest{ + Share: share, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}}, + } + + _, err := s.UpdateReceivedShare(ctx, r) + if err != nil { + return nil, err + } + + return &provider.DeleteResponse{ + Status: status.NewOK(ctx), + }, nil + } + } + + return &provider.DeleteResponse{ + Status: status.NewNotFound(ctx, "could not find share"), + }, nil + } + + if s.isShareChild(ctx, p) { + shareName, shareChild := s.splitShare(ctx, p) + log.Debug().Msgf("path:%s sharename:%s sharechild: %s", p, shareName, shareChild) + + ref := &provider.Reference{Path: shareName} + + statReq := &provider.StatRequest{Ref: ref} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &provider.DeleteResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.DeleteResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &provider.DeleteResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + err = s.webdavRefDelete(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &provider.DeleteResponse{ + Status: status.NewInternal(ctx, err, "gateway: error deleting resource on webdav host: "+p), + }, nil + } + return &provider.DeleteResponse{ + Status: status.NewOK(ctx), + }, nil + } + + // append child to target + req.Ref.Path = path.Join(ri.Path, shareChild) + return s.delete(ctx, req) + } + + panic("gateway: delete called on unknown path:" + p) } func (s *svc) delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { @@ -350,7 +829,118 @@ func (s *svc) delete(ctx context.Context, req *provider.DeleteRequest) (*provide } func (s *svc) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) { - return s.move(ctx, req) + log := appctx.GetLogger(ctx) + p, st := s.getPath(ctx, req.Source) + if st.Code != rpc.Code_CODE_OK { + return &provider.MoveResponse{ + Status: st, + }, nil + } + + dp, st := s.getPath(ctx, req.Destination) + if st.Code != rpc.Code_CODE_OK && st.Code != rpc.Code_CODE_NOT_FOUND { + return &provider.MoveResponse{ + Status: st, + }, nil + } + + if !s.inSharedFolder(ctx, p) && !s.inSharedFolder(ctx, dp) { + return s.move(ctx, req) + } + + // allow renaming the share folder, the mount point, not the target. + if s.isShareName(ctx, p) && s.isShareName(ctx, dp) { + log.Info().Msgf("gateway: move: renaming share mountpoint: from:%s to:%s", p, dp) + return s.move(ctx, req) + } + + // resolve references and check the ref points to the same base path, paranoia check. + if s.isShareChild(ctx, p) && s.isShareChild(ctx, dp) { + shareName, shareChild := s.splitShare(ctx, p) + dshareName, dshareChild := s.splitShare(ctx, dp) + log.Debug().Msgf("srcpath:%s dstpath:%s srcsharename:%s srcsharechild: %s dstsharename:%s dstsharechild:%s ", p, dp, shareName, shareChild, dshareName, dshareChild) + + srcStatReq := &provider.StatRequest{Ref: &provider.Reference{Path: shareName}} + srcStatRes, err := s.stat(ctx, srcStatReq) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+srcStatReq.Ref.String()), + }, nil + } + + if srcStatRes.Status.Code != rpc.Code_CODE_OK { + return &provider.MoveResponse{ + Status: srcStatRes.Status, + }, nil + } + + dstStatReq := &provider.StatRequest{Ref: &provider.Reference{Path: dshareName}} + dstStatRes, err := s.stat(ctx, dstStatReq) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+srcStatReq.Ref.String()), + }, nil + } + + if dstStatRes.Status.Code != rpc.Code_CODE_OK { + return &provider.MoveResponse{ + Status: srcStatRes.Status, + }, nil + } + + srcRi, srcProtocol, err := s.checkRef(ctx, srcStatRes.Info) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+srcStatRes.Info.Target, err), + }, nil + } + + if srcProtocol == "webdav" { + err = s.webdavRefMove(ctx, dstStatRes.Info.Target, shareChild, dshareChild) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewInternal(ctx, err, "gateway: error moving resource on webdav host: "+p), + }, nil + } + return &provider.MoveResponse{ + Status: status.NewOK(ctx), + }, nil + } + dstRi, dstProtocol, err := s.checkRef(ctx, dstStatRes.Info) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+srcStatRes.Info.Target, err), + }, nil + } + + if dstProtocol == "webdav" { + err = s.webdavRefMove(ctx, dstStatRes.Info.Target, shareChild, dshareChild) + if err != nil { + return &provider.MoveResponse{ + Status: status.NewInternal(ctx, err, "gateway: error moving resource on webdav host: "+p), + }, nil + } + return &provider.MoveResponse{ + Status: status.NewOK(ctx), + }, nil + } + + src := &provider.Reference{ + Path: path.Join(srcRi.Path, shareChild), + } + dst := &provider.Reference{ + Path: path.Join(dstRi.Path, dshareChild), + } + + req.Source = src + req.Destination = dst + + return s.move(ctx, req) + } + + return &provider.MoveResponse{ + Status: status.NewStatusFromErrType(ctx, "move", errtypes.BadRequest("gateway: move called on unknown path: "+p)), + }, nil } func (s *svc) move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) { @@ -465,55 +1055,146 @@ func (s *svc) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provi }, nil } - res, err := c.GetLock(ctx, req) - if err != nil { - if gstatus.Code(err) == codes.PermissionDenied { - return &provider.GetLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + res, err := c.GetLock(ctx, req) + if err != nil { + if gstatus.Code(err) == codes.PermissionDenied { + return &provider.GetLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + } + return nil, errors.Wrap(err, "gateway: error calling GetLock") + } + + return res, nil +} + +// RefreshLock refreshes an existing lock on the given reference. +func (s *svc) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) { + c, err := s.find(ctx, req.Ref) + if err != nil { + return &provider.RefreshLockResponse{ + Status: status.NewStatusFromErrType(ctx, "RefreshLock ref="+req.Ref.String(), err), + }, nil + } + + res, err := c.RefreshLock(ctx, req) + if err != nil { + if gstatus.Code(err) == codes.PermissionDenied { + return &provider.RefreshLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + } + return nil, errors.Wrap(err, "gateway: error calling RefreshLock") + } + + return res, nil +} + +// Unlock removes an existing lock from the given reference. +func (s *svc) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) { + c, err := s.find(ctx, req.Ref) + if err != nil { + return &provider.UnlockResponse{ + Status: status.NewStatusFromErrType(ctx, "Unlock ref="+req.Ref.String(), err), + }, nil + } + + res, err := c.Unlock(ctx, req) + if err != nil { + if gstatus.Code(err) == codes.PermissionDenied { + return &provider.UnlockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + } + return nil, errors.Wrap(err, "gateway: error calling Unlock") + } + + return res, nil +} + +func (s *svc) statHome(ctx context.Context) (*provider.StatResponse, error) { + statRes, err := s.stat(ctx, &provider.StatRequest{Ref: &provider.Reference{Path: s.getHome(ctx)}}) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating home"), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: statRes.Status, + }, nil + } + + statSharedFolder, err := s.statSharesFolder(ctx) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating shares folder"), + }, nil + } + if statSharedFolder.Status.Code != rpc.Code_CODE_OK { + // If shares folder is not found, skip updating the etag + if statSharedFolder.Status.Code == rpc.Code_CODE_NOT_FOUND { + return statRes, nil + } + // otherwise return stat of share folder + return &provider.StatResponse{ + Status: statSharedFolder.Status, + }, nil + } + + if etagIface, err := s.etagCache.Get(statRes.Info.Owner.OpaqueId + ":" + statRes.Info.Path); err == nil { + resMtime := utils.TSToTime(statRes.Info.Mtime) + resEtag := etagIface.(etagWithTS) + // Use the updated etag if the home folder has been modified + if resMtime.Before(resEtag.Timestamp) { + statRes.Info.Etag = resEtag.Etag + } + } else { + statRes.Info.Etag = etag.GenerateEtagFromResources(statRes.Info, []*provider.ResourceInfo{statSharedFolder.Info}) + if s.c.EtagCacheTTL > 0 { + _ = s.etagCache.Set(statRes.Info.Owner.OpaqueId+":"+statRes.Info.Path, etagWithTS{statRes.Info.Etag, time.Now()}) } - return nil, errors.Wrap(err, "gateway: error calling GetLock") } - return res, nil + return statRes, nil } -// RefreshLock refreshes an existing lock on the given reference. -func (s *svc) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) { - c, err := s.find(ctx, req.Ref) +func (s *svc) statSharesFolder(ctx context.Context) (*provider.StatResponse, error) { + statRes, err := s.stat(ctx, &provider.StatRequest{Ref: &provider.Reference{Path: s.getSharedFolder(ctx)}}) if err != nil { - return &provider.RefreshLockResponse{ - Status: status.NewStatusFromErrType(ctx, "RefreshLock ref="+req.Ref.String(), err), + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating shares folder"), }, nil } - res, err := c.RefreshLock(ctx, req) - if err != nil { - if gstatus.Code(err) == codes.PermissionDenied { - return &provider.RefreshLockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil - } - return nil, errors.Wrap(err, "gateway: error calling RefreshLock") + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: statRes.Status, + }, nil } - return res, nil -} - -// Unlock removes an existing lock from the given reference. -func (s *svc) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) { - c, err := s.find(ctx, req.Ref) + lsRes, err := s.listSharesFolder(ctx) if err != nil { - return &provider.UnlockResponse{ - Status: status.NewStatusFromErrType(ctx, "Unlock ref="+req.Ref.String(), err), + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing shares folder"), + }, nil + } + if lsRes.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: lsRes.Status, }, nil } - res, err := c.Unlock(ctx, req) - if err != nil { - if gstatus.Code(err) == codes.PermissionDenied { - return &provider.UnlockResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + if etagIface, err := s.etagCache.Get(statRes.Info.Owner.OpaqueId + ":" + statRes.Info.Path); err == nil { + resMtime := utils.TSToTime(statRes.Info.Mtime) + resEtag := etagIface.(etagWithTS) + // Use the updated etag if the shares folder has been modified, i.e., a new + // reference has been created. + if resMtime.Before(resEtag.Timestamp) { + statRes.Info.Etag = resEtag.Etag + } + } else { + statRes.Info.Etag = etag.GenerateEtagFromResources(statRes.Info, lsRes.Infos) + if s.c.EtagCacheTTL > 0 { + _ = s.etagCache.Set(statRes.Info.Owner.OpaqueId+":"+statRes.Info.Path, etagWithTS{statRes.Info.Etag, time.Now()}) } - return nil, errors.Wrap(err, "gateway: error calling Unlock") } - - return res, nil + return statRes, nil } func (s *svc) stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) { @@ -564,7 +1245,147 @@ func (s *svc) Stat(ctx context.Context, req *provider.StatRequest) (*provider.St if utils.IsRelativeReference(req.Ref) { return s.stat(ctx, req) } - return s.stat(ctx, req) + + p := "" + var res *provider.StatResponse + var err error + if utils.IsAbsolutePathReference(req.Ref) { + p = req.Ref.Path + } else { + // Reference by just resource ID + // Stat it and store for future use + res, err = s.stat(ctx, req) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+req.Ref.String()), + }, nil + } + if res != nil && res.Status.Code != rpc.Code_CODE_OK { + return res, nil + } + p = res.Info.Path + } + + if path.Clean(p) == s.getHome(ctx) { + return s.statHome(ctx) + } + + if s.isSharedFolder(ctx, p) { + return s.statSharesFolder(ctx) + } + + if !s.inSharedFolder(ctx, p) { + if res != nil { + return res, nil + } + return s.stat(ctx, req) + } + + // we need to provide the info of the target, not the reference. + if s.isShareName(ctx, p) { + // If we haven't returned an error by now and res is nil, it means that + // req is an absolute path based ref, so we didn't stat it previously. + // So stat it now + if res == nil { + res, err = s.stat(ctx, req) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+req.Ref.String()), + }, nil + } + + if res.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: res.Status, + }, nil + } + } + + ri, protocol, err := s.checkRef(ctx, res.Info) + if err != nil { + return &provider.StatResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+res.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + ri, err = s.webdavRefStat(ctx, res.Info.Target) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error resolving webdav reference: "+p), + }, nil + } + } + + // we need to make sure we don't expose the reference target in the resource + // information. For example, if requests comes to: /home/MyShares/photos and photos + // is reference to /user/peter/Holidays/photos, we need to still return to the user + // /home/MyShares/photos + orgPath := res.Info.Path + res.Info = ri + res.Info.Path = orgPath + return res, nil + } + + if s.isShareChild(ctx, p) { + shareName, shareChild := s.splitShare(ctx, p) + + statReq := &provider.StatRequest{Ref: &provider.Reference{Path: shareName}} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &provider.StatResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + ri, err = s.webdavRefStat(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error resolving webdav reference: "+p), + }, nil + } + ri.Path = p + return &provider.StatResponse{ + Status: status.NewOK(ctx), + Info: ri, + }, nil + } + + // append child to target + req.Ref.Path = path.Join(ri.Path, shareChild) + res, err := s.stat(ctx, req) + if err != nil { + return &provider.StatResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating ref:"+req.Ref.String()), + }, nil + } + if res.Status.Code != rpc.Code_CODE_OK { + return &provider.StatResponse{ + Status: res.Status, + }, nil + } + + // we need to make sure we don't expose the reference target in the resource + // information. + res.Info.Path = p + return res, nil + } + + panic("gateway: stating an unknown path:" + p) } func (s *svc) checkRef(ctx context.Context, ri *provider.ResourceInfo) (*provider.ResourceInfo, string, error) { @@ -646,6 +1467,83 @@ func (s *svc) ListContainerStream(_ *provider.ListContainerStreamRequest, _ gate return errtypes.NotSupported("Unimplemented") } +func (s *svc) listHome(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { + lcr, err := s.listContainer(ctx, &provider.ListContainerRequest{ + Ref: &provider.Reference{Path: s.getHome(ctx)}, + ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, + }) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing home"), + }, nil + } + if lcr.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: lcr.Status, + }, nil + } + + for i := range lcr.Infos { + if s.isSharedFolder(ctx, lcr.Infos[i].GetPath()) { + statSharedFolder, err := s.statSharesFolder(ctx) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating shares folder"), + }, nil + } + if statSharedFolder.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: statSharedFolder.Status, + }, nil + } + lcr.Infos[i] = statSharedFolder.Info + break + } + } + + return lcr, nil +} + +func (s *svc) listSharesFolder(ctx context.Context) (*provider.ListContainerResponse, error) { + lcr, err := s.listContainer(ctx, &provider.ListContainerRequest{Ref: &provider.Reference{Path: s.getSharedFolder(ctx)}}) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing shared folder"), + }, nil + } + if lcr.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: lcr.Status, + }, nil + } + checkedInfos := make([]*provider.ResourceInfo, 0) + for i := range lcr.Infos { + info, protocol, err := s.checkRef(ctx, lcr.Infos[i]) + if err != nil { + // create status to log the proper messages + // this might arise when the shared resource has been moved to the recycle bin + // this might arise when the resource was unshared, but the share reference was not removed + status.NewStatusFromErrType(ctx, "error resolving reference "+lcr.Infos[i].Target, err) + // continue on errors so the user can see a list of the working shares + continue + } + + if protocol == "webdav" { + info, err = s.webdavRefStat(ctx, lcr.Infos[i].Target) + if err != nil { + // Might be the case that the webdav token has expired + continue + } + } + + info.Path = lcr.Infos[i].Path + checkedInfos = append(checkedInfos, info) + } + lcr.Infos = checkedInfos + + return lcr, nil +} + func (s *svc) filterProvidersByUserAgent(ctx context.Context, providers []*registry.ProviderInfo) []*registry.ProviderInfo { cat, ok := appctx.ContextGetUserAgentCategory(ctx) if !ok { @@ -762,7 +1660,183 @@ func (s *svc) listContainerAcrossProviders(ctx context.Context, req *provider.Li } func (s *svc) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { - return s.listContainer(ctx, req) + log := appctx.GetLogger(ctx) + + if utils.IsRelativeReference(req.Ref) { + return s.listContainer(ctx, req) + } + + p, st := s.getPath(ctx, req.Ref, req.ArbitraryMetadataKeys...) + if st.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: st, + }, nil + } + + if path.Clean(p) == s.getHome(ctx) { + return s.listHome(ctx, req) + } + + if s.isSharedFolder(ctx, p) { + return s.listSharesFolder(ctx) + } + + if !s.inSharedFolder(ctx, p) { + return s.listContainer(ctx, req) + } + + // we need to provide the info of the target, not the reference. + if s.isShareName(ctx, p) { + statReq := &provider.StatRequest{Ref: &provider.Reference{Path: p}} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating share:"+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + infos, err := s.webdavRefLs(ctx, statRes.Info.Target) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing webdav reference: "+p), + }, nil + } + + for _, info := range infos { + base := path.Base(info.Path) + info.Path = path.Join(p, base) + } + return &provider.ListContainerResponse{ + Status: status.NewOK(ctx), + Infos: infos, + }, nil + } + + if ri.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER { + err := errtypes.NotSupported("gateway: list container: cannot list non-container type:" + ri.Path) + log.Err(err).Msg("gateway: error listing") + return &provider.ListContainerResponse{ + Status: status.NewInvalidArg(ctx, "resource is not a container"), + }, nil + } + + newReq := &provider.ListContainerRequest{ + Ref: &provider.Reference{Path: ri.Path}, + ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, + } + newRes, err := s.listContainer(ctx, newReq) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing "+newReq.Ref.String()), + }, nil + } + + if newRes.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: newRes.Status, + }, nil + } + + // paths needs to be converted + for _, info := range newRes.Infos { + base := path.Base(info.Path) + info.Path = path.Join(p, base) + } + + return newRes, nil + } + + if s.isShareChild(ctx, p) { + shareName, shareChild := s.splitShare(ctx, p) + + statReq := &provider.StatRequest{Ref: &provider.Reference{Path: shareName}} + statRes, err := s.stat(ctx, statReq) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error stating share child "+statReq.Ref.String()), + }, nil + } + + if statRes.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: statRes.Status, + }, nil + } + + ri, protocol, err := s.checkRef(ctx, statRes.Info) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewStatusFromErrType(ctx, "error resolving reference "+statRes.Info.Target, err), + }, nil + } + + if protocol == "webdav" { + infos, err := s.webdavRefLs(ctx, statRes.Info.Target, shareChild) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing webdav reference: "+p), + }, nil + } + + for _, info := range infos { + base := path.Base(info.Path) + info.Path = path.Join(shareName, shareChild, base) + } + return &provider.ListContainerResponse{ + Status: status.NewOK(ctx), + Infos: infos, + }, nil + } + + if ri.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER { + err := errtypes.NotSupported("gateway: list container: cannot list non-container type:" + ri.Path) + log.Err(err).Msg("gateway: error listing") + return &provider.ListContainerResponse{ + Status: status.NewInvalidArg(ctx, "resource is not a container"), + }, nil + } + + newReq := &provider.ListContainerRequest{ + Ref: &provider.Reference{Path: path.Join(ri.Path, shareChild)}, + ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, + } + newRes, err := s.listContainer(ctx, newReq) + if err != nil { + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "gateway: error listing "+newReq.Ref.String()), + }, nil + } + + if newRes.Status.Code != rpc.Code_CODE_OK { + return &provider.ListContainerResponse{ + Status: newRes.Status, + }, nil + } + + // paths needs to be converted + for _, info := range newRes.Infos { + base := path.Base(info.Path) + info.Path = path.Join(shareName, shareChild, base) + } + + return newRes, nil + } + + panic("gateway: stating an unknown path:" + p) } func (s *svc) getPath(ctx context.Context, ref *provider.Reference, keys ...string) (string, *rpc.Status) { @@ -849,6 +1923,12 @@ func (s *svc) splitPath(_ context.Context, p string) []string { return strings.SplitN(p, "/", 4) // ["home", "MyShares", "photos", "Ibiza/beach.png"] } +func (s *svc) getSharedFolder(ctx context.Context) string { + home := s.getHome(ctx) + shareFolder := path.Join(home, s.c.ShareFolder) + return shareFolder +} + func (s *svc) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) { return &provider.CreateSymlinkResponse{ Status: status.NewUnimplemented(ctx, errtypes.NotSupported("CreateSymlink not implemented"), "CreateSymlink not implemented"), diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index f1c4a7d75a..fe90c334b4 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -175,10 +175,6 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { return nil, err } - if fs == nil { - return nil, errors.New("error creating fs driver") - } - // parse data server url u, err := url.Parse(c.DataServerURL) if err != nil { @@ -928,6 +924,17 @@ func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest, func (s *service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) { newRef, err := s.unwrap(ctx, req.Ref) + if err != nil { + // The path might be a virtual view; handle that case + if utils.IsAbsolutePathReference(req.Ref) && strings.HasPrefix(s.mountPath, req.Ref.Path) { + return s.listVirtualView(ctx, req.Ref) + } + + return &provider.ListContainerResponse{ + Status: status.NewInternal(ctx, err, "error unwrapping path"), + }, nil + } + mds, err := s.storage.ListFolder(ctx, newRef, req.ArbitraryMetadataKeys) if err != nil { var st *rpc.Status @@ -1544,8 +1551,7 @@ func (s *service) unwrap(ctx context.Context, ref *provider.Reference) (*provide func (s *service) trimMountPrefix(fn string) (string, error) { if strings.HasPrefix(fn, s.mountPath) { - p := path.Join("/", strings.TrimPrefix(fn, s.mountPath)) - return p, nil + return path.Join("/", strings.TrimPrefix(fn, s.mountPath)), nil } return "", errtypes.BadRequest(fmt.Sprintf("path=%q does not belong to this storage provider mount path=%q", fn, s.mountPath)) } diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 0b14453dfb..4e996c1ee4 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -30,6 +30,7 @@ import ( "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" @@ -41,6 +42,7 @@ import ( "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/storage/favorite" "github.com/cs3org/reva/pkg/storage/favorite/registry" + "github.com/cs3org/reva/pkg/storage/utils/templates" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/pkg/errors" ) @@ -289,22 +291,19 @@ func (s *svc) getClient() (gateway.GatewayAPIClient, error) { } func applyLayout(ctx context.Context, ns string, useLoggedInUserNS bool, requestPath string) string { - return ns // If useLoggedInUserNS is false, that implies that the request is coming from // the FilesHandler method invoked by a /dav/files/fileOwner where fileOwner // is not the same as the logged in user. In that case, we'll treat fileOwner // as the username whose files are to be accessed and use that in the // namespace template. - /* - u, ok := appctx.ContextGetUser(ctx) - if !ok || !useLoggedInUserNS { - requestUserID, _ := router.ShiftPath(requestPath) - u = &userpb.User{ - Username: requestUserID, - } + u, ok := appctx.ContextGetUser(ctx) + if !ok || !useLoggedInUserNS { + requestUserID, _ := router.ShiftPath(requestPath) + u = &userpb.User{ + Username: requestUserID, } - return templates.WithUser(u, ns) - */ + } + return templates.WithUser(u, ns) } func addAccessHeaders(w http.ResponseWriter, r *http.Request) { diff --git a/internal/http/services/owncloud/ocs/data/capabilities.go b/internal/http/services/owncloud/ocs/data/capabilities.go index 18a93c8676..7fc6dcaedf 100644 --- a/internal/http/services/owncloud/ocs/data/capabilities.go +++ b/internal/http/services/owncloud/ocs/data/capabilities.go @@ -53,7 +53,7 @@ type Capabilities struct { Core *CapabilitiesCore `json:"core" xml:"core"` Checksums *CapabilitiesChecksums `json:"checksums" xml:"checksums"` Files *CapabilitiesFiles `json:"files" mapstructure:"files" xml:"files"` - Dav *CapabilitiesDav `json:"dav" mapstructure:"dav" xml:"dav"` + Dav *CapabilitiesDav `json:"dav" xml:"dav"` FilesSharing *CapabilitiesFilesSharing `json:"files_sharing" mapstructure:"files_sharing" xml:"files_sharing"` Spaces *Spaces `json:"spaces,omitempty" mapstructure:"spaces" xml:"spaces,omitempty"` diff --git a/internal/http/services/owncloud/ocs/handlers/cloud/capabilities/uploads.go b/internal/http/services/owncloud/ocs/handlers/cloud/capabilities/uploads.go index 014575a92d..26d3ddfb6c 100644 --- a/internal/http/services/owncloud/ocs/handlers/cloud/capabilities/uploads.go +++ b/internal/http/services/owncloud/ocs/handlers/cloud/capabilities/uploads.go @@ -49,41 +49,32 @@ func (h *Handler) getCapabilitiesForUserAgent(_ context.Context, userAgent strin } func setCapabilitiesForChunkProtocol(cp chunkProtocol, c *data.Capabilities) { - // 2.7+ will use Chunking V1 if "capabilities > files > bigfilechunking" is "true" AND "capabilities > dav > chunking" is not there - c.Files.BigFileChunking = true - c.Dav = nil - c.Files.TusSupport = nil - /* - switch cp { - case chunkV1: + switch cp { + case chunkV1: + // 2.7+ will use Chunking V1 if "capabilities > files > bigfilechunking" is "true" AND "capabilities > dav > chunking" is not there + c.Files.BigFileChunking = true + c.Dav = nil + c.Files.TusSupport = nil - // 2.7+ will use Chunking V1 if "capabilities > files > bigfilechunking" is "true" AND "capabilities > dav > chunking" is not there - c.Files.BigFileChunking = true - c.Dav = nil - c.Files.TusSupport = nil + case chunkNG: + // 2.7+ will use Chunking NG if "capabilities > files > bigfilechunking" is "true" AND "capabilities > dav > chunking" = 1.0 + c.Files.BigFileChunking = true + c.Dav.Chunking = "1.0" + c.Files.TusSupport = nil - case chunkNG: + case chunkTUS: + // 2.7+ will use TUS if "capabilities > files > bigfilechunking" is "false" AND "capabilities > dav > chunking" = "" AND "capabilities > files > tus_support" has proper entries. + c.Files.BigFileChunking = false + c.Dav.Chunking = "" - // 2.7+ will use Chunking NG if "capabilities > files > bigfilechunking" is "true" AND "capabilities > dav > chunking" = 1.0 - c.Files.BigFileChunking = true - c.Dav.Chunking = "1.0" - c.Files.TusSupport = nil - - case chunkTUS: - - // 2.7+ will use TUS if "capabilities > files > bigfilechunking" is "false" AND "capabilities > dav > chunking" = "" AND "capabilities > files > tus_support" has proper entries. - c.Files.BigFileChunking = false - c.Dav.Chunking = "" - - // TODO: infer from various TUS handlers from all known storages - // until now we take the manually configured tus options - // c.Capabilities.Files.TusSupport = &data.CapabilitiesFilesTusSupport{ - // Version: "1.0.0", - // Resumable: "1.0.0", - // Extension: "creation,creation-with-upload", - // MaxChunkSize: 0, - // HTTPMethodOverride: "", - // } - } - */ + // TODO: infer from various TUS handlers from all known storages + // until now we take the manually configured tus options + // c.Capabilities.Files.TusSupport = &data.CapabilitiesFilesTusSupport{ + // Version: "1.0.0", + // Resumable: "1.0.0", + // Extension: "creation,creation-with-upload", + // MaxChunkSize: 0, + // HTTPMethodOverride: "", + // } + } }