diff --git a/internal/grpc/services/gateway/appprovider.go b/internal/grpc/services/gateway/appprovider.go index 22d1706d5a0..8af43a12377 100644 --- a/internal/grpc/services/gateway/appprovider.go +++ b/internal/grpc/services/gateway/appprovider.go @@ -20,15 +20,15 @@ package gateway import ( "context" - "fmt" + "net/url" + "strings" providerpb "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" registry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" - provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -37,19 +37,36 @@ import ( ) func (s *svc) OpenFileInAppProvider(ctx context.Context, req *gateway.OpenFileInAppProviderRequest) (*providerpb.OpenFileInAppProviderResponse, error) { + p, st := s.getPath(ctx, req.Ref) + if st.Code != rpc.Code_CODE_OK { + if st.Code == rpc.Code_CODE_NOT_FOUND { + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewNotFound(ctx, "gateway: file not found:"+req.Ref.String()), + }, nil + } + return &providerpb.OpenFileInAppProviderResponse{ + Status: st, + }, nil + } - accessToken, ok := tokenpkg.ContextGetToken(ctx) - if !ok || accessToken == "" { + if s.isSharedFolder(ctx, p) { return &providerpb.OpenFileInAppProviderResponse{ - Status: status.NewUnauthenticated(ctx, errors.New("Access token is invalid or empty"), ""), + Status: status.NewInvalid(ctx, "gateway: can't open shares folder"), }, nil } - statReq := &provider.StatRequest{ - Ref: req.Ref, + resName, resChild := p, "" + if s.isShareChild(ctx, p) { + resName, resChild = s.splitShare(ctx, p) } - statRes, err := s.Stat(ctx, statReq) + statRes, err := s.stat(ctx, &storageprovider.StatRequest{ + Ref: &storageprovider.Reference{ + Spec: &storageprovider.Reference_Path{ + Path: resName, + }, + }, + }) if err != nil { return &providerpb.OpenFileInAppProviderResponse{ Status: status.NewInternal(ctx, err, "gateway: error calling Stat on the resource path for the app provider: "+req.Ref.GetPath()), @@ -64,7 +81,98 @@ func (s *svc) OpenFileInAppProvider(ctx context.Context, req *gateway.OpenFileIn fileInfo := statRes.Info - provider, err := s.findAppProvider(ctx, fileInfo) + // The file is a share + if fileInfo.Type == storageprovider.ResourceType_RESOURCE_TYPE_REFERENCE { + uri, err := url.Parse(fileInfo.Target) + if err != nil { + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewInternal(ctx, err, "gateway: error parsing target uri: "+fileInfo.Target), + }, nil + } + if uri.Scheme == "webdav" { + return s.openFederatedShares(ctx, fileInfo.Target, req.ViewMode, resChild) + } + + res, err := s.Stat(ctx, &storageprovider.StatRequest{ + Ref: req.Ref, + }) + if err != nil { + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewInternal(ctx, err, "gateway: error calling Stat on the resource path for the app provider: "+req.Ref.GetPath()), + }, nil + } + if res.Status.Code != rpc.Code_CODE_OK { + err := status.NewErrorFromCode(res.Status.GetCode(), "gateway") + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewInternal(ctx, err, "Stat failed on the resource path for the app provider: "+req.Ref.GetPath()), + }, nil + } + fileInfo = res.Info + } + return s.openLocalResources(ctx, fileInfo, req.ViewMode) +} + +func (s *svc) openFederatedShares(ctx context.Context, targetURL string, vm gateway.OpenFileInAppProviderRequest_ViewMode, + nameQueries ...string) (*providerpb.OpenFileInAppProviderResponse, error) { + targetURL, err := appendNameQuery(targetURL, nameQueries...) + if err != nil { + return nil, err + } + ep, err := s.extractEndpointInfo(ctx, targetURL) + if err != nil { + return nil, err + } + + ref := &storageprovider.Reference{ + Spec: &storageprovider.Reference_Path{ + Path: ep.filePath, + }, + } + appProviderReq := &gateway.OpenFileInAppProviderRequest{ + Ref: ref, + ViewMode: vm, + } + + meshProvider, err := s.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ + Domain: ep.endpoint, + }) + if err != nil { + return nil, errors.Wrap(err, "gateway: error calling GetInfoByDomain") + } + var gatewayEP string + for _, s := range meshProvider.ProviderInfo.Services { + if strings.ToLower(s.Endpoint.Type.Name) == "gateway" { + gatewayEP = s.Endpoint.Path + } + } + + gatewayClient, err := pool.GetGatewayServiceClient(gatewayEP) + if err != nil { + err = errors.Wrap(err, "gateway: error calling GetGatewayClient") + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewInternal(ctx, err, "error getting gateway client"), + }, nil + } + + ctx = tokenpkg.ContextSetToken(ctx, ep.token) + res, err := gatewayClient.OpenFileInAppProvider(ctx, appProviderReq) + if err != nil { + return nil, errors.Wrap(err, "gateway: error calling OpenFileInAppProvider") + } + return res, nil +} + +func (s *svc) openLocalResources(ctx context.Context, ri *storageprovider.ResourceInfo, + vm gateway.OpenFileInAppProviderRequest_ViewMode) (*providerpb.OpenFileInAppProviderResponse, error) { + + accessToken, ok := tokenpkg.ContextGetToken(ctx) + if !ok || accessToken == "" { + return &providerpb.OpenFileInAppProviderResponse{ + Status: status.NewUnauthenticated(ctx, errors.New("Access token is invalid or empty"), ""), + }, nil + } + + provider, err := s.findAppProvider(ctx, ri) if err != nil { err = errors.Wrap(err, "gateway: error calling findAppProvider") var st *rpc.Status @@ -86,14 +194,9 @@ func (s *svc) OpenFileInAppProvider(ctx context.Context, req *gateway.OpenFileIn }, nil } - // build the appProvider specific request with the required extra info that has been obtained - - log := appctx.GetLogger(ctx) - log.Debug().Msg(fmt.Sprintf("request: %s", req)) - appProviderReq := &providerpb.OpenFileInAppProviderRequest{ - ResourceInfo: fileInfo, - ViewMode: providerpb.OpenFileInAppProviderRequest_ViewMode(req.ViewMode), + ResourceInfo: ri, + ViewMode: providerpb.OpenFileInAppProviderRequest_ViewMode(vm), AccessToken: accessToken, } diff --git a/internal/grpc/services/gateway/ocmshareprovider.go b/internal/grpc/services/gateway/ocmshareprovider.go index 5cdb7e70d29..3063d40247f 100644 --- a/internal/grpc/services/gateway/ocmshareprovider.go +++ b/internal/grpc/services/gateway/ocmshareprovider.go @@ -22,9 +22,7 @@ import ( "context" "fmt" "path" - "strings" - ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" @@ -288,20 +286,6 @@ func (s *svc) createWebdavReference(ctx context.Context, share *ocm.Share) (*rpc log := appctx.GetLogger(ctx) - meshProvider, err := s.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ - Domain: share.Creator.Idp, - }) - if err != nil { - err := errors.Wrap(err, "gateway: error calling GetInfoByDomain") - return status.NewInternal(ctx, err, "error updating received share"), nil - } - var webdavEndpoint string - for _, s := range meshProvider.ProviderInfo.Services { - if strings.ToLower(s.Endpoint.Type.Name) == "webdav" { - webdavEndpoint = s.Endpoint.Path - } - } - var token string tokenOpaque, ok := share.Grantee.Opaque.Map["token"] if !ok { @@ -330,8 +314,8 @@ func (s *svc) createWebdavReference(ctx context.Context, share *ocm.Share) (*rpc createRefReq := &provider.CreateReferenceRequest{ Path: refPath, - // webdav is the scheme, token@webdav_endpoint the opaque part and the share name the query of the URL. - TargetUri: fmt.Sprintf("webdav:%s@%s?name=%s", token, webdavEndpoint, share.Name), + // webdav is the scheme, token@host the opaque part and the share name the query of the URL. + TargetUri: fmt.Sprintf("webdav://%s@%s?name=%s", token, share.Creator.Idp, share.Name), } c, err := s.findByPath(ctx, refPath) diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index 128c5cc6e5e..1a557a54df8 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -182,14 +182,6 @@ func (s *svc) InitiateFileDownload(ctx context.Context, req *provider.InitiateFi }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - err := errors.New(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) - log.Err(err).Msg("gateway: error creating container") - return &gateway.InitiateFileDownloadResponse{ - Status: status.NewInternal(ctx, err, "gateway: error creating container"), - }, nil - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { @@ -347,14 +339,6 @@ func (s *svc) InitiateFileUpload(ctx context.Context, req *provider.InitiateFile }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - err := errors.New(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) - log.Err(err).Msg("gateway: error creating container") - return &gateway.InitiateFileUploadResponse{ - Status: status.NewInternal(ctx, err, "gateway: error uploading"), - }, nil - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { @@ -558,14 +542,6 @@ func (s *svc) CreateContainer(ctx context.Context, req *provider.CreateContainer }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - err := errors.New(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) - log.Err(err).Msg("gateway: error creating container") - return &provider.CreateContainerResponse{ - Status: status.NewInternal(ctx, err, "gateway: error creating container"), - }, nil - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { @@ -706,14 +682,6 @@ func (s *svc) Delete(ctx context.Context, req *provider.DeleteRequest) (*provide }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - err := errors.New(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) - log.Err(err).Msg("gateway: error deleting") - return &provider.DeleteResponse{ - Status: status.NewInternal(ctx, err, "gateway: error deleting"), - }, nil - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { @@ -849,14 +817,6 @@ func (s *svc) Move(ctx context.Context, req *provider.MoveRequest) (*provider.Mo }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - err := errors.New(fmt.Sprintf("gateway: expected reference: got:%+v", statRes.Info)) - log.Err(err).Msg("gateway: error deleting") - return &provider.MoveResponse{ - Status: status.NewInternal(ctx, err, "gateway: error deleting"), - }, nil - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { @@ -1052,10 +1012,6 @@ func (s *svc) Stat(ctx context.Context, req *provider.StatRequest) (*provider.St }, nil } - if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_REFERENCE { - panic("gateway: a share name must be of type reference: ref:" + statRes.Info.Path) - } - ri, protocol, err := s.checkRef(ctx, statRes.Info) if err != nil { if _, ok := err.(errtypes.IsNotFound); ok { diff --git a/internal/grpc/services/gateway/webdavstorageprovider.go b/internal/grpc/services/gateway/webdavstorageprovider.go index 5c27014571e..a4e5a9e3b31 100644 --- a/internal/grpc/services/gateway/webdavstorageprovider.go +++ b/internal/grpc/services/gateway/webdavstorageprovider.go @@ -25,6 +25,7 @@ import ( "path" "strings" + ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/errtypes" @@ -45,7 +46,7 @@ func (s *svc) webdavRefStat(ctx context.Context, targetURL string, nameQueries . return nil, err } - ep, err := extractEndpointInfo(targetURL) + ep, err := s.extractEndpointInfo(ctx, targetURL) if err != nil { return nil, err } @@ -67,7 +68,7 @@ func (s *svc) webdavRefLs(ctx context.Context, targetURL string, nameQueries ... return nil, err } - ep, err := extractEndpointInfo(targetURL) + ep, err := s.extractEndpointInfo(ctx, targetURL) if err != nil { return nil, err } @@ -95,7 +96,7 @@ func (s *svc) webdavRefMkdir(ctx context.Context, targetURL string, nameQueries return err } - ep, err := extractEndpointInfo(targetURL) + ep, err := s.extractEndpointInfo(ctx, targetURL) if err != nil { return err } @@ -114,7 +115,7 @@ func (s *svc) webdavRefMove(ctx context.Context, targetURL, src, destination str if err != nil { return err } - srcEP, err := extractEndpointInfo(srcURL) + srcEP, err := s.extractEndpointInfo(ctx, srcURL) if err != nil { return err } @@ -123,7 +124,7 @@ func (s *svc) webdavRefMove(ctx context.Context, targetURL, src, destination str if err != nil { return err } - destEP, err := extractEndpointInfo(destURL) + destEP, err := s.extractEndpointInfo(ctx, destURL) if err != nil { return err } @@ -144,7 +145,7 @@ func (s *svc) webdavRefDelete(ctx context.Context, targetURL string, nameQueries return err } - ep, err := extractEndpointInfo(targetURL) + ep, err := s.extractEndpointInfo(ctx, targetURL) if err != nil { return err } @@ -164,7 +165,7 @@ func (s *svc) webdavRefTransferEndpoint(ctx context.Context, targetURL string, n return "", nil, err } - ep, err := extractEndpointInfo(targetURL) + ep, err := s.extractEndpointInfo(ctx, targetURL) if err != nil { return "", nil, err } @@ -183,21 +184,7 @@ func (s *svc) webdavRefTransferEndpoint(ctx context.Context, targetURL string, n }, nil } -func normalize(info *gowebdav.File) *provider.ResourceInfo { - return &provider.ResourceInfo{ - // TODO(ishank011): Add Id, PermissionSet, Owner - Path: info.Path(), - Type: getResourceType(info.IsDir()), - Etag: info.ETag(), - MimeType: info.ContentType(), - Size: uint64(info.Size()), - Mtime: &types.Timestamp{ - Seconds: uint64(info.ModTime().Unix()), - }, - } -} - -func extractEndpointInfo(targetURL string) (*webdavEndpoint, error) { +func (s *svc) extractEndpointInfo(ctx context.Context, targetURL string) (*webdavEndpoint, error) { if targetURL == "" { return nil, errors.New("gateway: ref target is an empty uri") } @@ -210,11 +197,19 @@ func extractEndpointInfo(targetURL string) (*webdavEndpoint, error) { return nil, errtypes.NotSupported("ref target does not have the webdav scheme") } - parts := strings.SplitN(uri.Opaque, "@", 2) - if len(parts) < 2 { - err := errors.New("gateway: webdav ref does not follow the layout token@webdav_endpoint?name " + targetURL) - return nil, err + meshProvider, err := s.GetInfoByDomain(ctx, &ocmprovider.GetInfoByDomainRequest{ + Domain: uri.Host, + }) + if err != nil { + return nil, errors.Wrap(err, "gateway: error calling GetInfoByDomain") } + var webdavEP string + for _, s := range meshProvider.ProviderInfo.Services { + if strings.ToLower(s.Endpoint.Type.Name) == "webdav" { + webdavEP = s.Endpoint.Path + } + } + m, err := url.ParseQuery(uri.RawQuery) if err != nil { return nil, errors.Wrap(err, "gateway: error parsing target resource name") @@ -222,11 +217,25 @@ func extractEndpointInfo(targetURL string) (*webdavEndpoint, error) { return &webdavEndpoint{ filePath: m["name"][0], - endpoint: parts[1], - token: parts[0], + endpoint: webdavEP, + token: uri.User.String(), }, nil } +func normalize(info *gowebdav.File) *provider.ResourceInfo { + return &provider.ResourceInfo{ + // TODO(ishank011): Add Id, PermissionSet, Owner + Path: info.Path(), + Type: getResourceType(info.IsDir()), + Etag: info.ETag(), + MimeType: info.ContentType(), + Size: uint64(info.Size()), + Mtime: &types.Timestamp{ + Seconds: uint64(info.ModTime().Unix()), + }, + } +} + func getResourceType(isDir bool) provider.ResourceType { if isDir { return provider.ResourceType_RESOURCE_TYPE_CONTAINER