github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/publicstorageprovider/publicstorageprovider.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  // Package publicstorageprovider provides a CS3 storageprovider implementation for public links.
    20  // It will list spaces with type `grant` and `mountpoint` when a public scope is present.
    21  package publicstorageprovider
    22  
    23  import (
    24  	"context"
    25  	"encoding/json"
    26  	"path"
    27  	"strings"
    28  
    29  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    30  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    31  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    32  	link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
    33  	ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
    34  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    35  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    36  	"github.com/cs3org/reva/v2/pkg/appctx"
    37  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    38  	"github.com/cs3org/reva/v2/pkg/errtypes"
    39  	"github.com/cs3org/reva/v2/pkg/rgrpc"
    40  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    41  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    42  	"github.com/cs3org/reva/v2/pkg/storagespace"
    43  	"github.com/cs3org/reva/v2/pkg/utils"
    44  	"github.com/mitchellh/mapstructure"
    45  	"github.com/pkg/errors"
    46  	"github.com/rs/zerolog"
    47  	"go.opentelemetry.io/otel/attribute"
    48  	"google.golang.org/grpc"
    49  	"google.golang.org/grpc/codes"
    50  	gstatus "google.golang.org/grpc/status"
    51  )
    52  
    53  // name is the Tracer name used to identify this instrumentation library.
    54  const tracerName = "publicstorageprovider"
    55  
    56  func init() {
    57  	rgrpc.Register("publicstorageprovider", New)
    58  }
    59  
    60  type config struct {
    61  	GatewayAddr string `mapstructure:"gateway_addr"`
    62  }
    63  
    64  type service struct {
    65  	conf            *config
    66  	gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
    67  }
    68  
    69  func (s *service) Close() error {
    70  	return nil
    71  }
    72  
    73  func (s *service) UnprotectedEndpoints() []string {
    74  	return []string{}
    75  }
    76  
    77  func (s *service) Register(ss *grpc.Server) {
    78  	provider.RegisterProviderAPIServer(ss, s)
    79  	provider.RegisterSpacesAPIServer(ss, s)
    80  }
    81  
    82  func parseConfig(m map[string]interface{}) (*config, error) {
    83  	c := &config{}
    84  	if err := mapstructure.Decode(m, c); err != nil {
    85  		err = errors.Wrap(err, "error decoding conf")
    86  		return nil, err
    87  	}
    88  	return c, nil
    89  }
    90  
    91  // New creates a new publicstorageprovider service.
    92  func New(m map[string]interface{}, ss *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) {
    93  	c, err := parseConfig(m)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	gatewaySelector, err := pool.GatewaySelector(c.GatewayAddr)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	service := &service{
   104  		conf:            c,
   105  		gatewaySelector: gatewaySelector,
   106  	}
   107  
   108  	return service, nil
   109  }
   110  
   111  func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) {
   112  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   113  	if err != nil {
   114  		return &provider.SetArbitraryMetadataResponse{
   115  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   116  		}, nil
   117  	}
   118  	gatewayClient, err := s.gatewaySelector.Next()
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{Opaque: req.Opaque, Ref: ref, ArbitraryMetadata: req.ArbitraryMetadata})
   123  }
   124  
   125  func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) {
   126  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   127  }
   128  
   129  // SetLock puts a lock on the given reference
   130  func (s *service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) {
   131  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   132  	if err != nil {
   133  		return &provider.SetLockResponse{
   134  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   135  		}, nil
   136  	}
   137  	gatewayClient, err := s.gatewaySelector.Next()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return gatewayClient.SetLock(ctx, &provider.SetLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock})
   142  }
   143  
   144  // GetLock returns an existing lock on the given reference
   145  func (s *service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) {
   146  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   147  	if err != nil {
   148  		return &provider.GetLockResponse{
   149  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   150  		}, nil
   151  	}
   152  	gatewayClient, err := s.gatewaySelector.Next()
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return gatewayClient.GetLock(ctx, &provider.GetLockRequest{Opaque: req.Opaque, Ref: ref})
   157  }
   158  
   159  // RefreshLock refreshes an existing lock on the given reference
   160  func (s *service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) {
   161  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   162  	if err != nil {
   163  		return &provider.RefreshLockResponse{
   164  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   165  		}, nil
   166  	}
   167  	gatewayClient, err := s.gatewaySelector.Next()
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	return gatewayClient.RefreshLock(ctx, &provider.RefreshLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock})
   172  }
   173  
   174  // Unlock removes an existing lock from the given reference
   175  func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) {
   176  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   177  	if err != nil {
   178  		return &provider.UnlockResponse{
   179  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   180  		}, nil
   181  	}
   182  	gatewayClient, err := s.gatewaySelector.Next()
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	return gatewayClient.Unlock(ctx, &provider.UnlockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock})
   187  }
   188  
   189  func (s *service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) {
   190  	statReq := &provider.StatRequest{Ref: req.Ref}
   191  	statRes, err := s.Stat(ctx, statReq)
   192  	if err != nil {
   193  		return &provider.InitiateFileDownloadResponse{
   194  			Status: status.NewInternal(ctx, "InitiateFileDownload: error stating ref:"+req.Ref.String()),
   195  		}, nil
   196  	}
   197  	if statRes.Status.Code != rpc.Code_CODE_OK {
   198  		if statRes.Status.Code == rpc.Code_CODE_NOT_FOUND {
   199  			return &provider.InitiateFileDownloadResponse{
   200  				Status: status.NewNotFound(ctx, "InitiateFileDownload: file not found"),
   201  			}, nil
   202  		}
   203  		return &provider.InitiateFileDownloadResponse{
   204  			Status: status.NewInternal(ctx, "InitiateFileDownload: error stating ref"),
   205  		}, nil
   206  	}
   207  
   208  	req.Opaque = statRes.Info.Opaque
   209  	return s.initiateFileDownload(ctx, req)
   210  }
   211  
   212  func (s *service) translatePublicRefToCS3Ref(ctx context.Context, ref *provider.Reference) (*provider.Reference, *provider.ResourceInfo, string, error) {
   213  	log := appctx.GetLogger(ctx)
   214  
   215  	info, _, _, token, err := s.extractLinkFromScope(ctx)
   216  	if err != nil {
   217  		return nil, nil, "", err
   218  	}
   219  
   220  	var path string
   221  	switch info.Type {
   222  	case provider.ResourceType_RESOURCE_TYPE_CONTAINER:
   223  		// folders point to the folder -> path needs to be added
   224  		path = utils.MakeRelativePath(ref.Path)
   225  	case provider.ResourceType_RESOURCE_TYPE_FILE:
   226  		// files already point to the correct id
   227  		path = "."
   228  	default:
   229  		// TODO: can this happen?
   230  		// path = utils.MakeRelativePath(relativePath)
   231  	}
   232  
   233  	cs3Ref := &provider.Reference{
   234  		ResourceId: info.Id,
   235  		Path:       path,
   236  	}
   237  
   238  	log.Debug().
   239  		Interface("sourceRef", ref).
   240  		Interface("cs3Ref", cs3Ref).
   241  		Str("tkn", token).
   242  		Str("originalPath", info.Path).
   243  		Str("relativePath", path).
   244  		Msg("translatePublicRefToCS3Ref")
   245  	return cs3Ref, info, token, nil
   246  }
   247  
   248  func (s *service) initiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) {
   249  	ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   250  	switch {
   251  	case err != nil:
   252  		return &provider.InitiateFileDownloadResponse{
   253  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   254  		}, nil
   255  	case info.PermissionSet == nil || !info.PermissionSet.InitiateFileDownload:
   256  		return &provider.InitiateFileDownloadResponse{
   257  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileDownload permission"),
   258  		}, nil
   259  	}
   260  	dReq := &provider.InitiateFileDownloadRequest{
   261  		Ref: ref,
   262  	}
   263  
   264  	gatewayClient, err := s.gatewaySelector.Next()
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	dRes, err := gatewayClient.InitiateFileDownload(ctx, dReq)
   269  	if err != nil {
   270  		return &provider.InitiateFileDownloadResponse{
   271  			Status: status.NewInternal(ctx, "initiateFileDownload: error calling InitiateFileDownload"),
   272  		}, nil
   273  	}
   274  
   275  	if dRes.Status.Code != rpc.Code_CODE_OK {
   276  		return &provider.InitiateFileDownloadResponse{
   277  			Status: dRes.Status,
   278  		}, nil
   279  	}
   280  
   281  	protocols := make([]*provider.FileDownloadProtocol, len(dRes.Protocols))
   282  	for p := range dRes.Protocols {
   283  		if !strings.HasSuffix(dRes.Protocols[p].DownloadEndpoint, "/") {
   284  			dRes.Protocols[p].DownloadEndpoint += "/"
   285  		}
   286  		dRes.Protocols[p].DownloadEndpoint += dRes.Protocols[p].Token
   287  
   288  		protocols = append(protocols, &provider.FileDownloadProtocol{
   289  			Opaque:           dRes.Protocols[p].Opaque,
   290  			Protocol:         dRes.Protocols[p].Protocol,
   291  			DownloadEndpoint: dRes.Protocols[p].DownloadEndpoint,
   292  			Expose:           true, // the gateway already has encoded the upload endpoint
   293  		})
   294  	}
   295  
   296  	return &provider.InitiateFileDownloadResponse{
   297  		Opaque:    dRes.GetOpaque(),
   298  		Status:    dRes.Status,
   299  		Protocols: protocols,
   300  	}, nil
   301  }
   302  
   303  func (s *service) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*provider.InitiateFileUploadResponse, error) {
   304  	cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   305  	switch {
   306  	case err != nil:
   307  		return &provider.InitiateFileUploadResponse{
   308  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   309  		}, nil
   310  	case info.PermissionSet == nil || !info.PermissionSet.InitiateFileUpload:
   311  		return &provider.InitiateFileUploadResponse{
   312  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileUpload permission"),
   313  		}, nil
   314  	}
   315  	uReq := &provider.InitiateFileUploadRequest{
   316  		Ref:    cs3Ref,
   317  		Opaque: req.Opaque,
   318  		LockId: req.LockId,
   319  	}
   320  
   321  	gatewayClient, err := s.gatewaySelector.Next()
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	uRes, err := gatewayClient.InitiateFileUpload(ctx, uReq)
   326  	if err != nil {
   327  		return &provider.InitiateFileUploadResponse{
   328  			Status: status.NewInternal(ctx, "InitiateFileUpload: error calling InitiateFileUpload"),
   329  		}, nil
   330  	}
   331  
   332  	if uRes.Status.Code != rpc.Code_CODE_OK {
   333  		return &provider.InitiateFileUploadResponse{
   334  			Status: uRes.Status,
   335  		}, nil
   336  	}
   337  
   338  	protocols := make([]*provider.FileUploadProtocol, len(uRes.Protocols))
   339  	for p := range uRes.Protocols {
   340  		if !strings.HasSuffix(uRes.Protocols[p].UploadEndpoint, "/") {
   341  			uRes.Protocols[p].UploadEndpoint += "/"
   342  		}
   343  		uRes.Protocols[p].UploadEndpoint += uRes.Protocols[p].Token
   344  
   345  		protocols = append(protocols, &provider.FileUploadProtocol{
   346  			Opaque:             uRes.Protocols[p].Opaque,
   347  			Protocol:           uRes.Protocols[p].Protocol,
   348  			UploadEndpoint:     uRes.Protocols[p].UploadEndpoint,
   349  			AvailableChecksums: uRes.Protocols[p].AvailableChecksums,
   350  			Expose:             true, // the gateway already has encoded the upload endpoint
   351  		})
   352  	}
   353  
   354  	res := &provider.InitiateFileUploadResponse{
   355  		Opaque:    uRes.GetOpaque(),
   356  		Status:    uRes.Status,
   357  		Protocols: protocols,
   358  	}
   359  
   360  	return res, nil
   361  }
   362  
   363  func (s *service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) {
   364  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   365  }
   366  
   367  func (s *service) GetHome(ctx context.Context, req *provider.GetHomeRequest) (*provider.GetHomeResponse, error) {
   368  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   369  }
   370  
   371  func (s *service) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) {
   372  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   373  }
   374  
   375  func (s *service) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
   376  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   377  }
   378  
   379  // ListStorageSpaces returns storage spaces when a public scope is present
   380  // in the context.
   381  //
   382  // On the one hand, it lists a `mountpoint` space that can be used by the
   383  // registry to construct a mount path. These spaces have their root
   384  // storageid set to 7993447f-687f-490d-875c-ac95e89a62a4 and the
   385  // opaqueid set to the link token.
   386  //
   387  // On the other hand, it lists a `grant` space for the shared resource id,
   388  // so id based requests can find the correct storage provider. These spaces
   389  // have their root set to the shared resource.
   390  func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) {
   391  	spaceTypes := map[string]struct{}{}
   392  	var exists = struct{}{}
   393  	appendTypes := []string{}
   394  	var spaceID *provider.ResourceId
   395  	for _, f := range req.Filters {
   396  		switch f.Type {
   397  		case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE:
   398  			spaceType := f.GetSpaceType()
   399  			if spaceType == "+mountpoint" || spaceType == "+grant" {
   400  				appendTypes = append(appendTypes, strings.TrimPrefix(spaceType, "+"))
   401  				continue
   402  			}
   403  			spaceTypes[spaceType] = exists
   404  		case provider.ListStorageSpacesRequest_Filter_TYPE_ID:
   405  			resID, err := storagespace.ParseID(f.GetId().GetOpaqueId())
   406  			if err != nil {
   407  				return &provider.ListStorageSpacesResponse{
   408  					Status: &rpc.Status{Code: rpc.Code_CODE_INVALID_ARGUMENT, Message: err.Error()},
   409  				}, nil
   410  			}
   411  			if resID.SpaceId != utils.PublicStorageSpaceID && resID.SpaceId != utils.OCMStorageSpaceID {
   412  				return &provider.ListStorageSpacesResponse{
   413  					// a specific id was requested, return not found instead of empty list
   414  					Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND},
   415  				}, nil
   416  			}
   417  			spaceID = &resID
   418  		}
   419  	}
   420  
   421  	info, share, grantee, token, err := s.extractLinkFromScope(ctx)
   422  	if err != nil {
   423  		switch err.(type) {
   424  		case errtypes.NotFound:
   425  			// if there is no public scope there are no publicstorage spaces
   426  			return &provider.ListStorageSpacesResponse{
   427  				Status: &rpc.Status{Code: rpc.Code_CODE_OK},
   428  			}, nil
   429  		default:
   430  			return &provider.ListStorageSpacesResponse{
   431  				Status: &rpc.Status{Code: rpc.Code_CODE_INTERNAL},
   432  			}, nil
   433  		}
   434  	}
   435  
   436  	if len(spaceTypes) == 0 {
   437  		spaceTypes["mountpoint"] = exists
   438  	}
   439  	for _, s := range appendTypes {
   440  		spaceTypes[s] = exists
   441  	}
   442  
   443  	res := &provider.ListStorageSpacesResponse{
   444  		Status: status.NewOK(ctx),
   445  	}
   446  	for k := range spaceTypes {
   447  		switch k {
   448  		case "grant":
   449  			// when a list storage space with the resourceid of an external
   450  			// resource is made we may have a grant for it
   451  			root := info.Id
   452  			if spaceID != nil && !utils.ResourceIDEqual(spaceID, root) {
   453  				// none of our business
   454  				continue
   455  			}
   456  			// we know a grant for this resource
   457  			space := &provider.StorageSpace{
   458  				Id: &provider.StorageSpaceId{
   459  					OpaqueId: storagespace.FormatResourceID(root),
   460  				},
   461  				SpaceType: "grant",
   462  				Owner:     &userv1beta1.User{Id: grantee},
   463  				// the publicstorageprovider keeps track of mount points
   464  				Root: root,
   465  			}
   466  
   467  			res.StorageSpaces = append(res.StorageSpaces, space)
   468  		case "mountpoint":
   469  			root := &provider.ResourceId{
   470  				StorageId: utils.PublicStorageProviderID,
   471  				SpaceId:   utils.PublicStorageSpaceID,
   472  				OpaqueId:  token, // the link share has no id, only the token
   473  			}
   474  			if ocmShare, ok := share.(*ocm.Share); ok {
   475  				root.OpaqueId = ocmShare.GetId().GetOpaqueId()
   476  			}
   477  			if spaceID != nil {
   478  				switch {
   479  				case utils.ResourceIDEqual(spaceID, root):
   480  					// we have a virtual node
   481  				case utils.ResourceIDEqual(spaceID, info.Id):
   482  					// we have a mount point
   483  					root = info.Id
   484  				default:
   485  					// none of our business
   486  					continue
   487  				}
   488  			}
   489  			space := &provider.StorageSpace{
   490  				Id: &provider.StorageSpaceId{
   491  					OpaqueId: storagespace.FormatResourceID(root),
   492  				},
   493  				SpaceType: "mountpoint",
   494  				Owner:     &userv1beta1.User{Id: grantee}, // FIXME actually, the mount point belongs to no one?
   495  				// the publicstorageprovider keeps track of mount points
   496  				Root: root,
   497  			}
   498  
   499  			res.StorageSpaces = append(res.StorageSpaces, space)
   500  		}
   501  	}
   502  	return res, nil
   503  }
   504  
   505  func (s *service) extractLinkFromScope(ctx context.Context) (*provider.ResourceInfo, interface{}, *userv1beta1.UserId, string, error) {
   506  	scopes, ok := ctxpkg.ContextGetScopes(ctx)
   507  	if !ok {
   508  		return nil, nil, nil, "", errtypes.NotFound("No scopes found in context")
   509  	}
   510  	for k, v := range scopes {
   511  		if strings.HasPrefix(k, "ocmshare:") && v.Resource.Decoder == "json" {
   512  			share := &ocm.Share{}
   513  			err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, share)
   514  			if err != nil {
   515  				return nil, nil, nil, "", errtypes.InternalError("failed to unmarshal ocm share")
   516  			}
   517  
   518  			// the share is minimally populated, we need more than the token
   519  			// look up complete share
   520  			info, resolvedShare, err := s.resolveToken(ctx, share)
   521  			if err != nil {
   522  				return nil, nil, nil, "", err
   523  			}
   524  			return info, resolvedShare, share.Owner, share.Token, nil
   525  		} else if strings.HasPrefix(k, "publicshare:") && v.Resource.Decoder == "json" {
   526  			share := &link.PublicShare{}
   527  			err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, share)
   528  			if err != nil {
   529  				return nil, nil, nil, "", errtypes.InternalError("failed to unmarshal public share")
   530  			}
   531  
   532  			// the share is minimally populated, we need more than the token
   533  			// look up complete share
   534  			info, resolvedShare, err := s.resolveToken(ctx, share)
   535  			if err != nil {
   536  				return nil, nil, nil, "", err
   537  			}
   538  			return info, resolvedShare, share.Owner, share.Token, nil
   539  		}
   540  	}
   541  	return nil, nil, nil, "", errtypes.NotFound("No public storage info found in scopes")
   542  }
   543  func (s *service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
   544  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   545  }
   546  
   547  func (s *service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) {
   548  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   549  }
   550  
   551  func (s *service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) {
   552  	ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "CreateContainer")
   553  	defer span.End()
   554  
   555  	span.SetAttributes(attribute.KeyValue{
   556  		Key:   "reference",
   557  		Value: attribute.StringValue(req.Ref.String()),
   558  	})
   559  
   560  	cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   561  	switch {
   562  	case err != nil:
   563  		return &provider.CreateContainerResponse{
   564  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   565  		}, nil
   566  	case info.PermissionSet == nil || !info.PermissionSet.CreateContainer:
   567  		return &provider.CreateContainerResponse{
   568  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant CreateContainer permission"),
   569  		}, nil
   570  	}
   571  
   572  	var res *provider.CreateContainerResponse
   573  	// the call has to be made to the gateway instead of the storage.
   574  	gatewayClient, err := s.gatewaySelector.Next()
   575  	if err != nil {
   576  		return nil, err
   577  	}
   578  	res, err = gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{
   579  		Ref: cs3Ref,
   580  	})
   581  	if err != nil {
   582  		return &provider.CreateContainerResponse{
   583  			Status: status.NewInternal(ctx, "createContainer: error calling CreateContainer for ref:"+req.Ref.String()),
   584  		}, nil
   585  	}
   586  	if res.Status.Code == rpc.Code_CODE_INTERNAL {
   587  		return res, nil
   588  	}
   589  
   590  	return res, nil
   591  }
   592  
   593  func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) {
   594  	ref, _, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   595  	if err != nil {
   596  		return &provider.TouchFileResponse{
   597  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   598  		}, nil
   599  	}
   600  	gatewayClient, err := s.gatewaySelector.Next()
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  	return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{Opaque: req.Opaque, Ref: ref})
   605  }
   606  
   607  func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) {
   608  	ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Delete")
   609  	defer span.End()
   610  
   611  	span.SetAttributes(attribute.KeyValue{
   612  		Key:   "reference",
   613  		Value: attribute.StringValue(req.Ref.String()),
   614  	})
   615  
   616  	cs3Ref, info, _, err := s.translatePublicRefToCS3Ref(ctx, req.Ref)
   617  	switch {
   618  	case err != nil:
   619  		return &provider.DeleteResponse{
   620  			Status: status.NewStatusFromErrType(ctx, "failed to resolve reference", err),
   621  		}, nil
   622  	case info.PermissionSet == nil || !info.PermissionSet.Delete:
   623  		return &provider.DeleteResponse{
   624  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant Delete permission"),
   625  		}, nil
   626  	}
   627  
   628  	var res *provider.DeleteResponse
   629  	// the call has to be made to the gateway instead of the storage.
   630  	gatewayClient, err := s.gatewaySelector.Next()
   631  	if err != nil {
   632  		return nil, err
   633  	}
   634  	res, err = gatewayClient.Delete(ctx, &provider.DeleteRequest{
   635  		Ref: cs3Ref,
   636  	})
   637  	if err != nil {
   638  		return &provider.DeleteResponse{
   639  			Status: status.NewInternal(ctx, "Delete: error calling Delete for ref:"+req.Ref.String()),
   640  		}, nil
   641  	}
   642  	if res.Status.Code == rpc.Code_CODE_INTERNAL {
   643  		return res, nil
   644  	}
   645  
   646  	return res, nil
   647  }
   648  
   649  func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) {
   650  	ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Move")
   651  	defer span.End()
   652  
   653  	span.SetAttributes(
   654  		attribute.KeyValue{
   655  			Key:   "source",
   656  			Value: attribute.StringValue(req.Source.String()),
   657  		},
   658  		attribute.KeyValue{
   659  			Key:   "destination",
   660  			Value: attribute.StringValue(req.Destination.String()),
   661  		},
   662  	)
   663  
   664  	cs3RefSource, info, tknSource, err := s.translatePublicRefToCS3Ref(ctx, req.Source)
   665  	switch {
   666  	case err != nil:
   667  		return &provider.MoveResponse{
   668  			Status: status.NewStatusFromErrType(ctx, "failed to resolve source reference", err),
   669  		}, nil
   670  	case info.PermissionSet == nil || !info.PermissionSet.Move:
   671  		return &provider.MoveResponse{
   672  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant Move permission"),
   673  		}, nil
   674  	}
   675  	// FIXME: maybe there's a shortcut possible here using the source path
   676  	cs3RefDestination, _, tknDest, err := s.translatePublicRefToCS3Ref(ctx, req.Destination)
   677  	if err != nil {
   678  		return &provider.MoveResponse{
   679  			Status: status.NewStatusFromErrType(ctx, "failed to resolve destination reference", err),
   680  		}, nil
   681  	}
   682  
   683  	if tknSource != tknDest {
   684  		return &provider.MoveResponse{
   685  			Status: status.NewInvalid(ctx, "Source and destination token must be the same"),
   686  		}, nil
   687  	}
   688  
   689  	var res *provider.MoveResponse
   690  	// the call has to be made to the gateway instead of the storage.
   691  	gatewayClient, err := s.gatewaySelector.Next()
   692  	if err != nil {
   693  		return nil, err
   694  	}
   695  	res, err = gatewayClient.Move(ctx, &provider.MoveRequest{
   696  		Source:      cs3RefSource,
   697  		Destination: cs3RefDestination,
   698  	})
   699  	if err != nil {
   700  		return &provider.MoveResponse{
   701  			Status: status.NewInternal(ctx, "Move: error calling Move for source ref "+req.Source.String()+" to destination ref "+req.Destination.String()),
   702  		}, nil
   703  	}
   704  	if res.Status.Code == rpc.Code_CODE_INTERNAL {
   705  		return res, nil
   706  	}
   707  
   708  	return res, nil
   709  }
   710  
   711  func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) {
   712  	ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Stat")
   713  	defer span.End()
   714  
   715  	span.SetAttributes(
   716  		attribute.KeyValue{
   717  			Key:   "source",
   718  			Value: attribute.StringValue(req.Ref.String()),
   719  		})
   720  
   721  	info, share, _, token, err := s.extractLinkFromScope(ctx)
   722  	if err != nil {
   723  		switch err.(type) {
   724  		case errtypes.NotFound:
   725  			return &provider.StatResponse{
   726  				Status: status.NewNotFound(ctx, "share or token not found"),
   727  			}, nil
   728  		default:
   729  			return &provider.StatResponse{
   730  				Status: status.NewInternal(ctx, "share or token not found"),
   731  			}, nil
   732  		}
   733  	}
   734  
   735  	if info.Type == provider.ResourceType_RESOURCE_TYPE_FILE || req.Ref.Path == "" {
   736  		res := &provider.StatResponse{
   737  			Status: status.NewOK(ctx),
   738  			Info:   info,
   739  		}
   740  		s.augmentStatResponse(ctx, res.Info, info, share, token)
   741  		return res, nil
   742  	}
   743  
   744  	ref := &provider.Reference{
   745  		ResourceId: info.Id,
   746  		Path:       utils.MakeRelativePath(req.Ref.Path),
   747  	}
   748  
   749  	gatewayClient, err := s.gatewaySelector.Next()
   750  	if err != nil {
   751  		return nil, err
   752  	}
   753  	statResponse, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: ref})
   754  	if err != nil {
   755  		return &provider.StatResponse{
   756  			Status: status.NewInternal(ctx, "Stat: error calling Stat for ref:"+req.Ref.String()),
   757  		}, nil
   758  	}
   759  
   760  	s.augmentStatResponse(ctx, statResponse.Info, info, share, token)
   761  
   762  	return statResponse, nil
   763  }
   764  
   765  func (s *service) augmentStatResponse(ctx context.Context, statInfo *provider.ResourceInfo, shareInfo *provider.ResourceInfo, share interface{}, tkn string) {
   766  	// prevent leaking internal paths
   767  	if statInfo != nil {
   768  		if err := addShare(statInfo, share); err != nil {
   769  			appctx.GetLogger(ctx).Error().Err(err).Interface("share", share).Interface("info", statInfo).Msg("error when adding share")
   770  		}
   771  
   772  		var sharePath string
   773  		if shareInfo.Type == provider.ResourceType_RESOURCE_TYPE_FILE {
   774  			sharePath = path.Base(shareInfo.Path)
   775  		} else {
   776  			sharePath = strings.TrimPrefix(statInfo.Path, shareInfo.Path)
   777  		}
   778  
   779  		statInfo.Path = path.Join("/", sharePath)
   780  		filterPermissions(statInfo.PermissionSet, shareInfo.PermissionSet)
   781  	}
   782  }
   783  
   784  func addShare(i *provider.ResourceInfo, share interface{}) error {
   785  	if i.Opaque == nil {
   786  		i.Opaque = &typesv1beta1.Opaque{}
   787  	}
   788  	if i.Opaque.Map == nil {
   789  		i.Opaque.Map = map[string]*typesv1beta1.OpaqueEntry{}
   790  	}
   791  	val, err := json.Marshal(share)
   792  	if err != nil {
   793  		return err
   794  	}
   795  	i.Opaque.Map["link-share"] = &typesv1beta1.OpaqueEntry{Decoder: "json", Value: val}
   796  	return nil
   797  }
   798  
   799  func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest, ss provider.ProviderAPI_ListContainerStreamServer) error {
   800  	return gstatus.Errorf(codes.Unimplemented, "method not implemented")
   801  }
   802  
   803  func (s *service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) {
   804  	info, share, _, _, err := s.extractLinkFromScope(ctx)
   805  	if err != nil {
   806  		switch err.(type) {
   807  		case errtypes.NotFound:
   808  			return &provider.ListContainerResponse{
   809  				Status: status.NewNotFound(ctx, "share or token not found"),
   810  			}, nil
   811  		default:
   812  			return &provider.ListContainerResponse{
   813  				Status: status.NewInternal(ctx, "share or token not found"),
   814  			}, nil
   815  		}
   816  	}
   817  	if info.PermissionSet == nil || !info.PermissionSet.ListContainer {
   818  		return &provider.ListContainerResponse{
   819  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant ListContainer permission"),
   820  		}, nil
   821  	}
   822  
   823  	gatewayClient, err := s.gatewaySelector.Next()
   824  	if err != nil {
   825  		return nil, err
   826  	}
   827  	listContainerR, err := gatewayClient.ListContainer(
   828  		ctx,
   829  		&provider.ListContainerRequest{
   830  			Ref: &provider.Reference{
   831  				ResourceId: info.Id,
   832  				// prefix relative path with './' to make it a CS3 relative path
   833  				Path: utils.MakeRelativePath(req.Ref.Path),
   834  			},
   835  		},
   836  	)
   837  	if err != nil {
   838  		return &provider.ListContainerResponse{
   839  			Status: status.NewInternal(ctx, "ListContainer: error calling ListContainer for ref:"+req.Ref.String()),
   840  		}, nil
   841  	}
   842  
   843  	for i := range listContainerR.Infos {
   844  		// FIXME how do we reduce permissions to what is granted by the public link?
   845  		// only a problem for id based access -> middleware
   846  		filterPermissions(listContainerR.Infos[i].PermissionSet, info.PermissionSet)
   847  		if err := addShare(listContainerR.Infos[i], share); err != nil {
   848  			appctx.GetLogger(ctx).Error().Err(err).Interface("share", share).Interface("info", listContainerR.Infos[i]).Msg("error when adding share")
   849  		}
   850  	}
   851  
   852  	return listContainerR, nil
   853  }
   854  
   855  func filterPermissions(l *provider.ResourcePermissions, r *provider.ResourcePermissions) {
   856  	l.AddGrant = l.AddGrant && r.AddGrant
   857  	l.CreateContainer = l.CreateContainer && r.CreateContainer
   858  	l.Delete = l.Delete && r.Delete
   859  	l.GetPath = l.GetPath && r.GetPath
   860  	l.GetQuota = l.GetQuota && r.GetQuota
   861  	l.InitiateFileDownload = l.InitiateFileDownload && r.InitiateFileDownload
   862  	l.InitiateFileUpload = l.InitiateFileUpload && r.InitiateFileUpload
   863  	l.ListContainer = l.ListContainer && r.ListContainer
   864  	l.ListFileVersions = l.ListFileVersions && r.ListFileVersions
   865  	l.ListGrants = l.ListGrants && r.ListGrants
   866  	l.ListRecycle = l.ListRecycle && r.ListRecycle
   867  	l.Move = l.Move && r.Move
   868  	l.PurgeRecycle = l.PurgeRecycle && r.PurgeRecycle
   869  	l.RemoveGrant = l.RemoveGrant && r.RemoveGrant
   870  	l.RestoreFileVersion = l.RestoreFileVersion && r.RestoreFileVersion
   871  	l.RestoreRecycleItem = l.RestoreRecycleItem && r.RestoreRecycleItem
   872  	l.Stat = l.Stat && r.Stat
   873  	l.UpdateGrant = l.UpdateGrant && r.UpdateGrant
   874  }
   875  
   876  func (s *service) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) {
   877  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   878  }
   879  
   880  func (s *service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) {
   881  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   882  }
   883  
   884  func (s *service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss provider.ProviderAPI_ListRecycleStreamServer) error {
   885  	return gstatus.Errorf(codes.Unimplemented, "method not implemented")
   886  }
   887  
   888  func (s *service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) {
   889  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   890  }
   891  
   892  func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) {
   893  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   894  }
   895  
   896  func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
   897  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   898  }
   899  
   900  func (s *service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) {
   901  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   902  }
   903  
   904  func (s *service) AddGrant(ctx context.Context, req *provider.AddGrantRequest) (*provider.AddGrantResponse, error) {
   905  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   906  }
   907  
   908  func (s *service) DenyGrant(ctx context.Context, req *provider.DenyGrantRequest) (*provider.DenyGrantResponse, error) {
   909  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   910  }
   911  
   912  func (s *service) CreateReference(ctx context.Context, req *provider.CreateReferenceRequest) (*provider.CreateReferenceResponse, error) {
   913  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   914  }
   915  
   916  func (s *service) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) {
   917  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   918  }
   919  
   920  func (s *service) UpdateGrant(ctx context.Context, req *provider.UpdateGrantRequest) (*provider.UpdateGrantResponse, error) {
   921  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   922  }
   923  
   924  func (s *service) RemoveGrant(ctx context.Context, req *provider.RemoveGrantRequest) (*provider.RemoveGrantResponse, error) {
   925  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   926  }
   927  
   928  func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) {
   929  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   930  }
   931  
   932  // resolveToken returns the resource info for the publicly shared resource.
   933  func (s *service) resolveToken(ctx context.Context, share interface{}) (*provider.ResourceInfo, interface{}, error) {
   934  	gatewayClient, err := s.gatewaySelector.Next()
   935  	if err != nil {
   936  		return nil, nil, err
   937  	}
   938  
   939  	resourceID := &provider.ResourceId{}
   940  	perms := &provider.ResourcePermissions{}
   941  	var resolvedShare interface{}
   942  	switch v := share.(type) {
   943  	case *link.PublicShare:
   944  		publicShareResponse, err := gatewayClient.GetPublicShare(
   945  			ctx,
   946  			&link.GetPublicShareRequest{
   947  				Ref: &link.PublicShareReference{
   948  					Spec: &link.PublicShareReference_Token{
   949  						Token: v.Token,
   950  					},
   951  				},
   952  				Sign: true,
   953  			},
   954  		)
   955  		switch {
   956  		case err != nil:
   957  			return nil, nil, err
   958  		case publicShareResponse.Status.Code != rpc.Code_CODE_OK:
   959  			return nil, nil, errtypes.NewErrtypeFromStatus(publicShareResponse.Status)
   960  		}
   961  		resolvedShare = publicShareResponse.GetShare()
   962  		resourceID = publicShareResponse.GetShare().GetResourceId()
   963  		perms = publicShareResponse.GetShare().GetPermissions().GetPermissions()
   964  	case *ocm.Share:
   965  		gsr, err := gatewayClient.GetOCMShareByToken(ctx, &ocm.GetOCMShareByTokenRequest{
   966  			Token: v.Token,
   967  		})
   968  		switch {
   969  		case err != nil:
   970  			return nil, nil, err
   971  		case gsr.Status.Code != rpc.Code_CODE_OK:
   972  			return nil, nil, errtypes.NewErrtypeFromStatus(gsr.Status)
   973  		}
   974  		accessMethods := gsr.GetShare().GetAccessMethods()
   975  		if len(accessMethods) == 0 {
   976  			return nil, nil, errtypes.PermissionDenied("failed to get access to the requested resource")
   977  		}
   978  		resolvedShare = gsr.GetShare()
   979  		resourceID = gsr.GetShare().GetResourceId()
   980  		perms = accessMethods[0].GetWebdavOptions().Permissions
   981  	}
   982  
   983  	sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
   984  		Ref: &provider.Reference{
   985  			ResourceId: resourceID,
   986  		},
   987  	})
   988  	switch {
   989  	case err != nil:
   990  		return nil, nil, err
   991  	case sRes.Status.Code != rpc.Code_CODE_OK:
   992  		return nil, nil, errtypes.NewErrtypeFromStatus(sRes.Status)
   993  	}
   994  
   995  	// Set permissions
   996  	sRes.Info.PermissionSet = perms
   997  	return sRes.Info, resolvedShare, nil
   998  }