github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.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 sharesstorageprovider
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"path/filepath"
    25  	"strings"
    26  
    27  	"github.com/cs3org/reva/v2/pkg/storagespace"
    28  	"github.com/rs/zerolog"
    29  	"golang.org/x/sync/errgroup"
    30  	"google.golang.org/genproto/protobuf/field_mask"
    31  	"google.golang.org/grpc"
    32  	codes "google.golang.org/grpc/codes"
    33  	gstatus "google.golang.org/grpc/status"
    34  	"google.golang.org/protobuf/types/known/fieldmaskpb"
    35  
    36  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    37  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    38  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    39  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    40  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    41  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    42  	"github.com/cs3org/reva/v2/pkg/appctx"
    43  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    44  	"github.com/cs3org/reva/v2/pkg/errtypes"
    45  	"github.com/cs3org/reva/v2/pkg/rgrpc"
    46  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    47  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    48  	"github.com/cs3org/reva/v2/pkg/sharedconf"
    49  	"github.com/cs3org/reva/v2/pkg/utils"
    50  	"github.com/mitchellh/mapstructure"
    51  	"github.com/pkg/errors"
    52  )
    53  
    54  const (
    55  	_defaultSharesJailEtag = "DECAFC00FEE"
    56  )
    57  
    58  func init() {
    59  	rgrpc.Register("sharesstorageprovider", NewDefault)
    60  }
    61  
    62  type config struct {
    63  	GatewayAddr               string `mapstructure:"gateway_addr"`
    64  	UserShareProviderEndpoint string `mapstructure:"usershareprovidersvc"`
    65  	MaxConcurrency            int    `mapstructure:"max_concurrency"`
    66  }
    67  
    68  type service struct {
    69  	gatewaySelector              pool.Selectable[gateway.GatewayAPIClient]
    70  	sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient]
    71  	maxConcurrency               int
    72  }
    73  
    74  func (s *service) Close() error {
    75  	return nil
    76  }
    77  
    78  func (s *service) UnprotectedEndpoints() []string {
    79  	return []string{}
    80  }
    81  
    82  func (s *service) Register(ss *grpc.Server) {
    83  	provider.RegisterProviderAPIServer(ss, s)
    84  	provider.RegisterSpacesAPIServer(ss, s)
    85  }
    86  
    87  // NewDefault returns a new instance of the SharesStorageProvider service with default dependencies
    88  func NewDefault(m map[string]interface{}, _ *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) {
    89  	c := &config{}
    90  	if err := mapstructure.Decode(m, c); err != nil {
    91  		err = errors.Wrap(err, "error decoding conf")
    92  		return nil, err
    93  	}
    94  
    95  	gatewaySelector, err := pool.GatewaySelector(sharedconf.GetGatewaySVC(c.GatewayAddr))
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	sharingCollaborationSelector, err := pool.SharingCollaborationSelector(sharedconf.GetGatewaySVC(c.UserShareProviderEndpoint))
   101  	if err != nil {
   102  		return nil, errors.Wrap(err, "sharesstorageprovider: error getting UserShareProvider client")
   103  	}
   104  
   105  	if c.MaxConcurrency <= 0 {
   106  		c.MaxConcurrency = 5
   107  	}
   108  
   109  	return New(gatewaySelector, sharingCollaborationSelector, c.MaxConcurrency)
   110  }
   111  
   112  // New returns a new instance of the SharesStorageProvider service
   113  func New(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient], maxConcurrency int) (rgrpc.Service, error) {
   114  	s := &service{
   115  		gatewaySelector:              gatewaySelector,
   116  		sharingCollaborationSelector: sharingCollaborationSelector,
   117  		maxConcurrency:               maxConcurrency,
   118  	}
   119  	return s, nil
   120  }
   121  
   122  func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArbitraryMetadataRequest) (*provider.SetArbitraryMetadataResponse, error) {
   123  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   124  	appctx.GetLogger(ctx).Debug().
   125  		Interface("ref", req.Ref).
   126  		Interface("received_share", receivedShare).
   127  		Msg("sharesstorageprovider: Got SetArbitraryMetadata request")
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	if rpcStatus.Code != rpc.Code_CODE_OK {
   132  		return &provider.SetArbitraryMetadataResponse{
   133  			Status: rpcStatus,
   134  		}, nil
   135  	}
   136  
   137  	gatewayClient, err := s.gatewaySelector.Next()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{
   143  		Opaque:            req.Opaque,
   144  		Ref:               buildReferenceInShare(req.Ref, receivedShare),
   145  		ArbitraryMetadata: req.ArbitraryMetadata,
   146  	})
   147  }
   148  
   149  func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) {
   150  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   151  	appctx.GetLogger(ctx).Debug().
   152  		Interface("ref", req.Ref).
   153  		Interface("received_share", receivedShare).
   154  		Msg("sharesstorageprovider: Got UnsetArbitraryMetadata request")
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	if rpcStatus.Code != rpc.Code_CODE_OK {
   159  		return &provider.UnsetArbitraryMetadataResponse{
   160  			Status: rpcStatus,
   161  		}, nil
   162  	}
   163  
   164  	gatewayClient, err := s.gatewaySelector.Next()
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return gatewayClient.UnsetArbitraryMetadata(ctx, &provider.UnsetArbitraryMetadataRequest{
   170  		Opaque:                req.Opaque,
   171  		Ref:                   buildReferenceInShare(req.Ref, receivedShare),
   172  		ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
   173  	})
   174  }
   175  
   176  func (s *service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) {
   177  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   178  	appctx.GetLogger(ctx).Debug().
   179  		Interface("ref", req.Ref).
   180  		Interface("received_share", receivedShare).
   181  		Msg("sharesstorageprovider: Got InitiateFileDownload request")
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	if rpcStatus.Code != rpc.Code_CODE_OK {
   186  		return &provider.InitiateFileDownloadResponse{
   187  			Status: rpcStatus,
   188  		}, nil
   189  	}
   190  
   191  	gatewayClient, err := s.gatewaySelector.Next()
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	gwres, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{
   197  		Opaque: req.Opaque,
   198  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
   199  		LockId: req.LockId,
   200  	})
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	if gwres.Status.Code != rpc.Code_CODE_OK {
   205  		return &provider.InitiateFileDownloadResponse{
   206  			Status: gwres.Status,
   207  		}, nil
   208  	}
   209  
   210  	protocols := []*provider.FileDownloadProtocol{}
   211  	for p := range gwres.Protocols {
   212  		if !strings.HasSuffix(gwres.Protocols[p].DownloadEndpoint, "/") {
   213  			gwres.Protocols[p].DownloadEndpoint += "/"
   214  		}
   215  		gwres.Protocols[p].DownloadEndpoint += gwres.Protocols[p].Token
   216  
   217  		protocols = append(protocols, &provider.FileDownloadProtocol{
   218  			Opaque:           gwres.Protocols[p].Opaque,
   219  			Protocol:         gwres.Protocols[p].Protocol,
   220  			DownloadEndpoint: gwres.Protocols[p].DownloadEndpoint,
   221  			Expose:           true, // the gateway already has encoded the upload endpoint
   222  		})
   223  	}
   224  
   225  	return &provider.InitiateFileDownloadResponse{
   226  		Opaque:    gwres.GetOpaque(),
   227  		Status:    gwres.Status,
   228  		Protocols: protocols,
   229  	}, nil
   230  
   231  }
   232  
   233  func (s *service) InitiateFileUpload(ctx context.Context, req *provider.InitiateFileUploadRequest) (*provider.InitiateFileUploadResponse, error) {
   234  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   235  	appctx.GetLogger(ctx).Debug().
   236  		Interface("ref", req.Ref).
   237  		Interface("received_share", receivedShare).
   238  		Msg("sharesstorageprovider: Got InitiateFileUpload request")
   239  	switch {
   240  	case err != nil:
   241  		return nil, err
   242  	case rpcStatus.Code == rpc.Code_CODE_NOT_FOUND:
   243  		// the user has access (it showed up in the clist of shares), but we cannot write here
   244  		return &provider.InitiateFileUploadResponse{
   245  			Status: status.NewFailedPrecondition(ctx, nil, rpcStatus.GetMessage()),
   246  		}, nil
   247  	case rpcStatus.Code != rpc.Code_CODE_OK:
   248  		return &provider.InitiateFileUploadResponse{
   249  			Status: rpcStatus,
   250  		}, nil
   251  	}
   252  
   253  	if !receivedShare.GetShare().GetPermissions().GetPermissions().GetInitiateFileUpload() {
   254  		return &provider.InitiateFileUploadResponse{
   255  			Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileDownload permission"),
   256  		}, nil
   257  	}
   258  
   259  	gatewayClient, err := s.gatewaySelector.Next()
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  
   264  	gwres, err := gatewayClient.InitiateFileUpload(ctx, &provider.InitiateFileUploadRequest{
   265  		Opaque:  req.Opaque,
   266  		Ref:     buildReferenceInShare(req.Ref, receivedShare),
   267  		LockId:  req.LockId,
   268  		Options: req.Options,
   269  	})
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	if gwres.Status.Code != rpc.Code_CODE_OK {
   274  		return &provider.InitiateFileUploadResponse{
   275  			Status: gwres.Status,
   276  		}, nil
   277  	}
   278  
   279  	protocols := []*provider.FileUploadProtocol{}
   280  	for p := range gwres.Protocols {
   281  		if !strings.HasSuffix(gwres.Protocols[p].UploadEndpoint, "/") {
   282  			gwres.Protocols[p].UploadEndpoint += "/"
   283  		}
   284  		gwres.Protocols[p].UploadEndpoint += gwres.Protocols[p].Token
   285  
   286  		protocols = append(protocols, &provider.FileUploadProtocol{
   287  			Opaque:             gwres.Protocols[p].Opaque,
   288  			Protocol:           gwres.Protocols[p].Protocol,
   289  			UploadEndpoint:     gwres.Protocols[p].UploadEndpoint,
   290  			AvailableChecksums: gwres.Protocols[p].AvailableChecksums,
   291  			Expose:             true, // the gateway already has encoded the upload endpoint
   292  		})
   293  	}
   294  	return &provider.InitiateFileUploadResponse{
   295  		Opaque:    gwres.GetOpaque(),
   296  		Status:    gwres.Status,
   297  		Protocols: protocols,
   298  	}, nil
   299  }
   300  
   301  func (s *service) GetPath(ctx context.Context, req *provider.GetPathRequest) (*provider.GetPathResponse, error) {
   302  	// TODO: Needs to find a path for a given resourceID
   303  	// It should
   304  	// - getPath of the resourceID - probably requires owner permissions -> needs machine auth
   305  	// - getPath of every received share on the same space - needs also owner permissions -> needs machine auth
   306  	// - find the shortest root path that is a prefix of the resource path
   307  	// alternatively implement this on storageprovider - it needs to know about grants to do so
   308  
   309  	if isShareJailRoot(req.ResourceId) {
   310  		return &provider.GetPathResponse{
   311  			Status: status.NewOK(ctx),
   312  			Path:   "/",
   313  		}, nil
   314  	}
   315  
   316  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, &provider.Reference{
   317  		ResourceId: req.ResourceId,
   318  	})
   319  	appctx.GetLogger(ctx).Debug().
   320  		Interface("resourceId", req.ResourceId).
   321  		Interface("received_share", receivedShare).
   322  		Msg("sharesstorageprovider: Got GetPath request")
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  	if rpcStatus.Code != rpc.Code_CODE_OK {
   327  		return &provider.GetPathResponse{
   328  			Status: rpcStatus,
   329  		}, nil
   330  	}
   331  
   332  	return &provider.GetPathResponse{
   333  		Status: status.NewOK(ctx),
   334  		Path:   filepath.Clean("/" + receivedShare.MountPoint.Path),
   335  	}, nil
   336  
   337  }
   338  
   339  func (s *service) GetHome(ctx context.Context, req *provider.GetHomeRequest) (*provider.GetHomeResponse, error) {
   340  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   341  }
   342  
   343  func (s *service) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (*provider.CreateHomeResponse, error) {
   344  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   345  }
   346  
   347  func (s *service) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
   348  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   349  }
   350  
   351  // ListStorageSpaces returns a list storage spaces with type "share" the current user has acces to.
   352  // Do owners of shares see type "shared"? Do they see andyhing? They need to if the want a fast lookup of shared with others
   353  // -> but then a storage sprovider has to do everything? not everything but permissions (= shares) related operations, yes
   354  // The root node of every storag space is the (spaceid, nodeid) of the shared node.
   355  // Since real space roots have (spaceid=nodeid) shares can be correlated with the space using the (spaceid, ) part of the reference.
   356  
   357  // However, when the space registry tries
   358  // to find a storage provider for a specific space it returns an empty list, so the actual storage provider
   359  // should be found.
   360  
   361  func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) {
   362  	spaceTypes := map[string]struct{}{}
   363  	var exists = struct{}{}
   364  	var fetchShares bool
   365  	appendTypes := []string{}
   366  	var spaceID *provider.ResourceId
   367  	for _, f := range req.Filters {
   368  		switch f.Type {
   369  		case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE:
   370  			spaceType := f.GetSpaceType()
   371  			// do we need to fetch the shares?
   372  			if spaceType == "+mountpoint" || spaceType == "+grant" {
   373  				appendTypes = append(appendTypes, strings.TrimPrefix(spaceType, "+"))
   374  				fetchShares = true
   375  				continue
   376  			}
   377  			if spaceType == "mountpoint" || spaceType == "grant" {
   378  				fetchShares = true
   379  			}
   380  			spaceTypes[spaceType] = exists
   381  		case provider.ListStorageSpacesRequest_Filter_TYPE_ID:
   382  			storageid, spaceid, shareid, err := storagespace.SplitID(f.GetId().OpaqueId)
   383  			if err != nil {
   384  				continue
   385  			}
   386  			if spaceid != utils.ShareStorageSpaceID {
   387  				return &provider.ListStorageSpacesResponse{
   388  					// a specific id was requested, return not found instead of empty list
   389  					Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND},
   390  				}, nil
   391  			}
   392  
   393  			spaceID = &provider.ResourceId{StorageId: storageid, SpaceId: spaceid, OpaqueId: shareid}
   394  		}
   395  	}
   396  
   397  	if len(spaceTypes) == 0 {
   398  		spaceTypes["virtual"] = exists
   399  		spaceTypes["mountpoint"] = exists
   400  		fetchShares = true
   401  	}
   402  
   403  	for _, s := range appendTypes {
   404  		spaceTypes[s] = exists
   405  	}
   406  
   407  	var receivedShares []*collaboration.ReceivedShare
   408  	var shareInfo map[string]*provider.ResourceInfo
   409  	var err error
   410  	if fetchShares {
   411  		receivedShares, shareInfo, err = s.fetchAcceptedShares(ctx, req.Opaque, []string{}, &fieldmaskpb.FieldMask{ /*TODO mtime and etag only?*/ })
   412  		if err != nil {
   413  			return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
   414  		}
   415  	}
   416  
   417  	res := &provider.ListStorageSpacesResponse{
   418  		Status: status.NewOK(ctx),
   419  	}
   420  	for k := range spaceTypes {
   421  		switch k {
   422  		case "virtual":
   423  			virtualRootID := &provider.ResourceId{
   424  				StorageId: utils.ShareStorageProviderID,
   425  				SpaceId:   utils.ShareStorageSpaceID,
   426  				OpaqueId:  utils.ShareStorageSpaceID,
   427  			}
   428  			if spaceID == nil || isShareJailRoot(spaceID) {
   429  				earliestShare := findEarliestShare(receivedShares, shareInfo)
   430  				var opaque *typesv1beta1.Opaque
   431  				var mtime *typesv1beta1.Timestamp
   432  				if earliestShare != nil {
   433  					if info, ok := shareInfo[earliestShare.GetId().GetOpaqueId()]; ok {
   434  						mtime = info.Mtime
   435  						opaque = utils.AppendPlainToOpaque(opaque, "etag", info.Etag)
   436  					}
   437  				} else {
   438  					opaque = utils.AppendPlainToOpaque(opaque, "etag", _defaultSharesJailEtag)
   439  				}
   440  				// only display the shares jail if we have accepted shares
   441  				opaque = utils.AppendPlainToOpaque(opaque, "spaceAlias", "virtual/shares")
   442  				space := &provider.StorageSpace{
   443  					Opaque: opaque,
   444  					Id: &provider.StorageSpaceId{
   445  						OpaqueId: storagespace.FormatResourceID(virtualRootID),
   446  					},
   447  					SpaceType: "virtual",
   448  					//Owner:     &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient
   449  					// the sharesstorageprovider keeps track of mount points
   450  					Root:  virtualRootID,
   451  					Name:  "Shares",
   452  					Mtime: mtime,
   453  				}
   454  				res.StorageSpaces = append(res.StorageSpaces, space)
   455  			}
   456  		case "grant":
   457  			for _, receivedShare := range receivedShares {
   458  				root := receivedShare.Share.ResourceId
   459  				// do we filter by id?
   460  				if spaceID != nil && !utils.ResourceIDEqual(spaceID, root) {
   461  					// none of our business
   462  					continue
   463  				}
   464  				// we know a grant for this resource
   465  				space := &provider.StorageSpace{
   466  					Id: &provider.StorageSpaceId{
   467  						OpaqueId: storagespace.FormatResourceID(root),
   468  					},
   469  					SpaceType: "grant",
   470  					Owner:     &userv1beta1.User{Id: receivedShare.Share.Owner},
   471  					// the sharesstorageprovider keeps track of mount points
   472  					Root:     root,
   473  					RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId],
   474  				}
   475  
   476  				res.StorageSpaces = append(res.StorageSpaces, space)
   477  			}
   478  		case "mountpoint":
   479  			for _, receivedShare := range receivedShares {
   480  				if receivedShare.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
   481  					continue
   482  				}
   483  				root := &provider.ResourceId{
   484  					StorageId: utils.ShareStorageProviderID,
   485  					SpaceId:   utils.ShareStorageSpaceID,
   486  					OpaqueId:  receivedShare.Share.Id.OpaqueId,
   487  				}
   488  				// do we filter by id
   489  				if spaceID != nil {
   490  					switch {
   491  					case utils.ResourceIDEqual(spaceID, root):
   492  						// we have a virtual node
   493  					case utils.ResourceIDEqual(spaceID, receivedShare.Share.ResourceId):
   494  						// we have a mount point
   495  						root = receivedShare.Share.ResourceId
   496  					default:
   497  						// none of our business
   498  						continue
   499  					}
   500  				}
   501  				var opaque *typesv1beta1.Opaque
   502  				if _, ok := shareInfo[receivedShare.Share.Id.OpaqueId]; !ok {
   503  					// we could not stat the share, skip it
   504  					continue
   505  				}
   506  				// add the resourceID for the grant
   507  				if receivedShare.Share.ResourceId != nil {
   508  					opaque = utils.AppendPlainToOpaque(opaque, "grantStorageID", receivedShare.Share.ResourceId.StorageId)
   509  					opaque = utils.AppendPlainToOpaque(opaque, "grantSpaceID", receivedShare.Share.ResourceId.SpaceId)
   510  					opaque = utils.AppendPlainToOpaque(opaque, "grantOpaqueID", receivedShare.Share.ResourceId.OpaqueId)
   511  				}
   512  
   513  				// prefix storageid if we are responsible
   514  				if root.SpaceId == utils.ShareStorageSpaceID {
   515  					root.StorageId = utils.ShareStorageProviderID
   516  				}
   517  
   518  				space := &provider.StorageSpace{
   519  					Opaque: opaque,
   520  					Id: &provider.StorageSpaceId{
   521  						OpaqueId: storagespace.FormatResourceID(root),
   522  					},
   523  					SpaceType: "mountpoint",
   524  					Owner:     &userv1beta1.User{Id: receivedShare.Share.Owner}, // FIXME actually, the mount point belongs to the recipient
   525  					// the sharesstorageprovider keeps track of mount points
   526  					Root:     root,
   527  					RootInfo: shareInfo[receivedShare.Share.Id.OpaqueId],
   528  				}
   529  
   530  				// TODO in the future the spaces registry will handle the alias for share spaces.
   531  				// for now use the name from the share to override the name determined by stat
   532  				if receivedShare.MountPoint != nil {
   533  					space.Name = receivedShare.MountPoint.Path
   534  					space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", space.SpaceType+"/"+strings.ReplaceAll(strings.ToLower(space.Name), " ", "-"))
   535  				}
   536  
   537  				// what if we don't have a name?
   538  				res.StorageSpaces = append(res.StorageSpaces, space)
   539  			}
   540  		}
   541  	}
   542  	return res, nil
   543  }
   544  
   545  func (s *service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
   546  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   547  }
   548  
   549  func (s *service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) {
   550  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   551  }
   552  
   553  func (s *service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) {
   554  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   555  	appctx.GetLogger(ctx).Debug().
   556  		Interface("ref", req.Ref).
   557  		Interface("received_share", receivedShare).
   558  		Msg("sharesstorageprovider: Got CreateContainer request")
   559  	if err != nil {
   560  		return nil, err
   561  	}
   562  	if rpcStatus.Code != rpc.Code_CODE_OK {
   563  		return &provider.CreateContainerResponse{
   564  			Status: rpcStatus,
   565  		}, nil
   566  	}
   567  
   568  	gatewayClient, err := s.gatewaySelector.Next()
   569  	if err != nil {
   570  		return nil, err
   571  	}
   572  
   573  	return gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{
   574  		Opaque: req.Opaque,
   575  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
   576  	})
   577  }
   578  
   579  func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) {
   580  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   581  	appctx.GetLogger(ctx).Debug().
   582  		Interface("ref", req.Ref).
   583  		Interface("received_share", receivedShare).
   584  		Err(err).
   585  		Msg("sharesstorageprovider: Got Delete request")
   586  	if err != nil {
   587  		return nil, err
   588  	}
   589  	if rpcStatus.Code != rpc.Code_CODE_OK {
   590  		return &provider.DeleteResponse{
   591  			Status: rpcStatus,
   592  		}, nil
   593  	}
   594  
   595  	// the root of a share always has the path "."
   596  	if req.Ref.ResourceId.StorageId == utils.ShareStorageProviderID && req.Ref.ResourceId.SpaceId == utils.ShareStorageSpaceID && req.Ref.Path == "." {
   597  		err := s.rejectReceivedShare(ctx, receivedShare)
   598  		if err != nil {
   599  			return &provider.DeleteResponse{
   600  				Status: status.NewInternal(ctx, "sharesstorageprovider: error rejecting share"),
   601  			}, nil
   602  		}
   603  		return &provider.DeleteResponse{
   604  			Status: status.NewOK(ctx),
   605  		}, nil
   606  	}
   607  
   608  	gatewayClient, err := s.gatewaySelector.Next()
   609  	if err != nil {
   610  		return nil, err
   611  	}
   612  
   613  	return gatewayClient.Delete(ctx, &provider.DeleteRequest{
   614  		Opaque: req.Opaque,
   615  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
   616  	})
   617  }
   618  
   619  func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) {
   620  	appctx.GetLogger(ctx).Debug().
   621  		Interface("source", req.Source).
   622  		Interface("destination", req.Destination).
   623  		Msg("sharesstorageprovider: Got Move request")
   624  
   625  	// TODO moving inside a shared tree should just be a forward of the move
   626  	//      but when do we rename a mounted share? Does that request even hit us?
   627  	//      - the registry needs to invalidate the alias
   628  	//      - the rhe share manager needs to change the name
   629  	//      ... but which storageprovider will receive the move request???
   630  	srcReceivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Source)
   631  	if err != nil {
   632  		return nil, err
   633  	}
   634  	if rpcStatus.Code != rpc.Code_CODE_OK {
   635  		return &provider.MoveResponse{
   636  			Status: rpcStatus,
   637  		}, nil
   638  	}
   639  
   640  	// we can do a rename
   641  	if isRename(req.Source, req.Destination) {
   642  
   643  		// Change the MountPoint of the share, it has no relative prefix
   644  		srcReceivedShare.MountPoint = &provider.Reference{
   645  			// FIXME actually it does have a resource id: the one of the sharesstorageprovider
   646  			Path: filepath.Base(req.Destination.Path),
   647  		}
   648  
   649  		sharingCollaborationClient, err := s.sharingCollaborationSelector.Next()
   650  		if err != nil {
   651  			return nil, err
   652  		}
   653  
   654  		_, err = sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{
   655  			Share:      srcReceivedShare,
   656  			UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}},
   657  		})
   658  		if err != nil {
   659  			return &provider.MoveResponse{
   660  				Status: status.NewInternal(ctx, "sharesstorageprovider: can not change mountpoint of share"),
   661  			}, nil
   662  		}
   663  		return &provider.MoveResponse{
   664  			Status: status.NewOK(ctx),
   665  		}, nil
   666  	}
   667  
   668  	dstReceivedShare, rpcStatus, err2 := s.resolveAcceptedShare(ctx, req.Destination)
   669  	if err2 != nil {
   670  		return nil, err2
   671  	}
   672  	if rpcStatus.Code != rpc.Code_CODE_OK {
   673  		return &provider.MoveResponse{
   674  			Status: rpcStatus,
   675  		}, nil
   676  	}
   677  
   678  	if dstReceivedShare.Share.Id.OpaqueId != srcReceivedShare.Share.Id.OpaqueId {
   679  		return &provider.MoveResponse{
   680  			Status: status.NewUnimplemented(ctx, nil, "cross storage moves are not supported, use copy and delete"),
   681  		}, nil
   682  	}
   683  
   684  	gatewayClient, err := s.gatewaySelector.Next()
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	return gatewayClient.Move(ctx, &provider.MoveRequest{
   690  		Opaque:      req.Opaque,
   691  		Source:      buildReferenceInShare(req.Source, srcReceivedShare),
   692  		Destination: buildReferenceInShare(req.Destination, dstReceivedShare),
   693  	})
   694  }
   695  
   696  // SetLock puts a lock on the given reference
   697  func (s *service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*provider.SetLockResponse, error) {
   698  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   699  }
   700  
   701  // GetLock returns an existing lock on the given reference
   702  func (s *service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*provider.GetLockResponse, error) {
   703  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   704  }
   705  
   706  // RefreshLock refreshes an existing lock on the given reference
   707  func (s *service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequest) (*provider.RefreshLockResponse, error) {
   708  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   709  }
   710  
   711  // Unlock removes an existing lock from the given reference
   712  func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*provider.UnlockResponse, error) {
   713  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   714  }
   715  
   716  func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provider.StatResponse, error) {
   717  	if isVirtualRoot(req.Ref) {
   718  		owner, ok := ctxpkg.ContextGetUser(ctx)
   719  		if !ok {
   720  			return nil, fmt.Errorf("missing user in context")
   721  		}
   722  		receivedShares, shareMd, err := s.fetchAcceptedShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask)
   723  		if err != nil {
   724  			return nil, err
   725  		}
   726  		earliestShare := findEarliestShare(receivedShares, shareMd)
   727  		var mtime *typesv1beta1.Timestamp
   728  		etag := _defaultSharesJailEtag
   729  		if earliestShare != nil {
   730  			if info, ok := shareMd[earliestShare.GetId().GetOpaqueId()]; ok {
   731  				mtime = info.Mtime
   732  				etag = info.Etag
   733  			}
   734  		}
   735  		return &provider.StatResponse{
   736  			Status: status.NewOK(ctx),
   737  			Info: &provider.ResourceInfo{
   738  				Opaque: &typesv1beta1.Opaque{
   739  					Map: map[string]*typesv1beta1.OpaqueEntry{
   740  						"root": {
   741  							Decoder: "plain",
   742  							Value:   []byte(utils.ShareStorageProviderID),
   743  						},
   744  					},
   745  				},
   746  				Id: &provider.ResourceId{
   747  					StorageId: utils.ShareStorageProviderID,
   748  					SpaceId:   utils.ShareStorageSpaceID,
   749  					OpaqueId:  utils.ShareStorageSpaceID,
   750  				},
   751  				Type:          provider.ResourceType_RESOURCE_TYPE_CONTAINER,
   752  				Mtime:         mtime,
   753  				Path:          "/",
   754  				MimeType:      "httpd/unix-directory",
   755  				Size:          0,
   756  				PermissionSet: &provider.ResourcePermissions{
   757  					// TODO
   758  				},
   759  				Space: &provider.StorageSpace{
   760  					SpaceType: "virtual",
   761  				},
   762  				Etag:  etag,
   763  				Owner: owner.Id,
   764  			},
   765  		}, nil
   766  	}
   767  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   768  	appctx.GetLogger(ctx).Debug().
   769  		Interface("ref", req.Ref).
   770  		Interface("received_share", receivedShare).
   771  		Err(err).
   772  		Msg("sharesstorageprovider: Got Stat request")
   773  	if err != nil {
   774  		return nil, err
   775  	}
   776  	if rpcStatus.Code != rpc.Code_CODE_OK {
   777  		return &provider.StatResponse{
   778  			Status: rpcStatus,
   779  		}, nil
   780  	}
   781  	if receivedShare.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
   782  		return &provider.StatResponse{
   783  			Status: &rpc.Status{Code: rpc.Code_CODE_NOT_FOUND},
   784  			// not mounted yet
   785  		}, nil
   786  	}
   787  
   788  	gatewayClient, err := s.gatewaySelector.Next()
   789  	if err != nil {
   790  		return nil, err
   791  	}
   792  
   793  	statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
   794  		Opaque:                req.Opaque,
   795  		Ref:                   buildReferenceInShare(req.Ref, receivedShare),
   796  		ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
   797  	})
   798  	if err != nil {
   799  		return nil, err
   800  	}
   801  
   802  	// when stating a share jail mountpoint we need to rewrite the ids
   803  	if statRes.GetStatus().GetCode() == rpc.Code_CODE_OK && receivedShare.MountPoint.Path == strings.TrimPrefix(req.Ref.Path, "./") && statRes.Info != nil {
   804  		// overwrite id with the share jail mountpoint id
   805  		statRes.Info.Id = &provider.ResourceId{
   806  			StorageId: utils.ShareStorageProviderID,
   807  			SpaceId:   utils.ShareStorageSpaceID,
   808  			OpaqueId:  receivedShare.GetShare().GetId().GetOpaqueId(),
   809  		}
   810  		// overwrite parent id with the share jail root
   811  		statRes.Info.ParentId = &provider.ResourceId{
   812  			StorageId: utils.ShareStorageProviderID,
   813  			SpaceId:   utils.ShareStorageSpaceID,
   814  			OpaqueId:  utils.ShareStorageSpaceID,
   815  		}
   816  	}
   817  
   818  	return statRes, nil
   819  }
   820  
   821  func (s *service) ListContainerStream(req *provider.ListContainerStreamRequest, ss provider.ProviderAPI_ListContainerStreamServer) error {
   822  	return gstatus.Errorf(codes.Unimplemented, "method not implemented")
   823  }
   824  func (s *service) ListContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) {
   825  	if isVirtualRoot(req.Ref) {
   826  		// The root is empty, it is filled by mountpoints
   827  		// so, when accessing the root via /dav/spaces, we need to list the accepted shares with their mountpoint
   828  
   829  		receivedShares, shareMd, err := s.fetchAcceptedShares(ctx, req.Opaque, req.ArbitraryMetadataKeys, req.FieldMask)
   830  		if err != nil {
   831  			return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
   832  		}
   833  
   834  		// Create map of shares that contains only the oldest share per shared resource. This is to avoid
   835  		// returning multiple resourceInfos for the same resource. But still be able to maintain a
   836  		// "somewhat" stable resourceID
   837  		oldestReceivedSharesByResourceID := make(map[string]*collaboration.ReceivedShare, len(receivedShares))
   838  		for _, receivedShare := range receivedShares {
   839  			if receivedShare.GetState() != collaboration.ShareState_SHARE_STATE_ACCEPTED {
   840  				continue
   841  			}
   842  			rIDStr := storagespace.FormatResourceID(receivedShare.GetShare().GetResourceId())
   843  			if oldest, ok := oldestReceivedSharesByResourceID[rIDStr]; ok {
   844  				// replace if older than current oldest
   845  				if utils.TSToTime(receivedShare.GetShare().GetCtime()).Before(utils.TSToTime(oldest.GetShare().GetCtime())) {
   846  					oldestReceivedSharesByResourceID[rIDStr] = receivedShare
   847  				}
   848  			} else {
   849  				oldestReceivedSharesByResourceID[rIDStr] = receivedShare
   850  			}
   851  		}
   852  
   853  		// now compose the resourceInfos for the unified list of shares
   854  		infos := []*provider.ResourceInfo{}
   855  		for _, share := range oldestReceivedSharesByResourceID {
   856  			info := shareMd[share.GetShare().GetId().GetOpaqueId()]
   857  			if info == nil {
   858  				appctx.GetLogger(ctx).Debug().
   859  					Interface("share", share).
   860  					Msg("sharesstorageprovider: no resource info for share")
   861  				continue
   862  			}
   863  
   864  			// override resource id info
   865  			info.Id = &provider.ResourceId{
   866  				StorageId: utils.ShareStorageProviderID,
   867  				SpaceId:   utils.ShareStorageSpaceID,
   868  				OpaqueId:  share.GetShare().GetId().GetOpaqueId(),
   869  			}
   870  			info.Path = filepath.Base(share.MountPoint.Path)
   871  			info.Name = info.Path
   872  
   873  			infos = append(infos, info)
   874  		}
   875  		return &provider.ListContainerResponse{
   876  			Status: status.NewOK(ctx),
   877  			Infos:  infos,
   878  		}, nil
   879  	}
   880  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   881  	appctx.GetLogger(ctx).Debug().
   882  		Interface("ref", req.Ref).
   883  		Interface("received_share", receivedShare).
   884  		Err(err).
   885  		Msg("sharesstorageprovider: Got ListContainer request")
   886  	if err != nil {
   887  		return nil, err
   888  	}
   889  	if rpcStatus.Code != rpc.Code_CODE_OK {
   890  		return &provider.ListContainerResponse{
   891  			Status: rpcStatus,
   892  		}, nil
   893  	}
   894  
   895  	gatewayClient, err := s.gatewaySelector.Next()
   896  	if err != nil {
   897  		return nil, err
   898  	}
   899  
   900  	return gatewayClient.ListContainer(ctx, &provider.ListContainerRequest{
   901  		Opaque:                req.Opaque,
   902  		Ref:                   buildReferenceInShare(req.Ref, receivedShare),
   903  		ArbitraryMetadataKeys: req.ArbitraryMetadataKeys,
   904  	})
   905  }
   906  func (s *service) ListFileVersions(ctx context.Context, req *provider.ListFileVersionsRequest) (*provider.ListFileVersionsResponse, error) {
   907  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   908  	appctx.GetLogger(ctx).Debug().
   909  		Interface("ref", req.Ref).
   910  		Interface("received_share", receivedShare).
   911  		Err(err).
   912  		Msg("sharesstorageprovider: Got ListFileVersions request")
   913  	if err != nil {
   914  		return nil, err
   915  	}
   916  	if rpcStatus.Code != rpc.Code_CODE_OK {
   917  		return &provider.ListFileVersionsResponse{
   918  			Status: rpcStatus,
   919  		}, nil
   920  	}
   921  
   922  	gatewayClient, err := s.gatewaySelector.Next()
   923  	if err != nil {
   924  		return nil, err
   925  	}
   926  
   927  	return gatewayClient.ListFileVersions(ctx, &provider.ListFileVersionsRequest{
   928  		Opaque: req.Opaque,
   929  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
   930  	})
   931  }
   932  
   933  func (s *service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) {
   934  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
   935  	appctx.GetLogger(ctx).Debug().
   936  		Interface("ref", req.Ref).
   937  		Interface("received_share", receivedShare).
   938  		Err(err).
   939  		Msg("sharesstorageprovider: Got RestoreFileVersion request")
   940  	if err != nil {
   941  		return nil, err
   942  	}
   943  	if rpcStatus.Code != rpc.Code_CODE_OK {
   944  		return &provider.RestoreFileVersionResponse{
   945  			Status: rpcStatus,
   946  		}, nil
   947  	}
   948  
   949  	gatewayClient, err := s.gatewaySelector.Next()
   950  	if err != nil {
   951  		return nil, err
   952  	}
   953  
   954  	return gatewayClient.RestoreFileVersion(ctx, &provider.RestoreFileVersionRequest{
   955  		Opaque: req.Opaque,
   956  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
   957  	})
   958  }
   959  
   960  func (s *service) ListRecycleStream(req *provider.ListRecycleStreamRequest, ss provider.ProviderAPI_ListRecycleStreamServer) error {
   961  	return gstatus.Errorf(codes.Unimplemented, "method not implemented")
   962  }
   963  
   964  func (s *service) ListRecycle(ctx context.Context, req *provider.ListRecycleRequest) (*provider.ListRecycleResponse, error) {
   965  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   966  }
   967  
   968  func (s *service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreRecycleItemRequest) (*provider.RestoreRecycleItemResponse, error) {
   969  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   970  }
   971  
   972  func (s *service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
   973  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   974  }
   975  
   976  func (s *service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) {
   977  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   978  }
   979  
   980  func (s *service) AddGrant(ctx context.Context, req *provider.AddGrantRequest) (*provider.AddGrantResponse, error) {
   981  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   982  }
   983  
   984  func (s *service) DenyGrant(ctx context.Context, ref *provider.DenyGrantRequest) (*provider.DenyGrantResponse, error) {
   985  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   986  }
   987  
   988  func (s *service) CreateReference(ctx context.Context, req *provider.CreateReferenceRequest) (*provider.CreateReferenceResponse, error) {
   989  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   990  }
   991  
   992  func (s *service) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) {
   993  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   994  }
   995  
   996  func (s *service) UpdateGrant(ctx context.Context, req *provider.UpdateGrantRequest) (*provider.UpdateGrantResponse, error) {
   997  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
   998  }
   999  
  1000  func (s *service) RemoveGrant(ctx context.Context, req *provider.RemoveGrantRequest) (*provider.RemoveGrantResponse, error) {
  1001  	return nil, gstatus.Errorf(codes.Unimplemented, "method not implemented")
  1002  }
  1003  
  1004  func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) {
  1005  	receivedShare, rpcStatus, err := s.resolveAcceptedShare(ctx, req.Ref)
  1006  	appctx.GetLogger(ctx).Debug().
  1007  		Interface("ref", req.Ref).
  1008  		Interface("received_share", receivedShare).
  1009  		Msg("sharesstorageprovider: Got TouchFile request")
  1010  	if err != nil {
  1011  		return nil, err
  1012  	}
  1013  	if rpcStatus.Code != rpc.Code_CODE_OK {
  1014  		return &provider.TouchFileResponse{
  1015  			Status: rpcStatus,
  1016  		}, nil
  1017  	}
  1018  
  1019  	gatewayClient, err := s.gatewaySelector.Next()
  1020  	if err != nil {
  1021  		return nil, err
  1022  	}
  1023  
  1024  	return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{
  1025  		Opaque: req.Opaque,
  1026  		Ref:    buildReferenceInShare(req.Ref, receivedShare),
  1027  	})
  1028  }
  1029  
  1030  // GetQuota returns 0 free quota. It is virtual ... the shares may have a different quota ...
  1031  func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) {
  1032  	// FIXME use req.Ref to get real quota
  1033  	return &provider.GetQuotaResponse{
  1034  		Status: status.NewOK(ctx),
  1035  	}, nil
  1036  }
  1037  
  1038  func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Reference) (*collaboration.ReceivedShare, *rpc.Status, error) {
  1039  	// treat absolute id based references as relative ones
  1040  	if ref.Path == "" {
  1041  		ref.Path = "."
  1042  	}
  1043  	if !utils.IsRelativeReference(ref) {
  1044  		return nil, status.NewInvalid(ctx, "sharesstorageprovider: can only handle relative references"), nil
  1045  	}
  1046  
  1047  	if ref.ResourceId.SpaceId != utils.ShareStorageSpaceID {
  1048  		return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil
  1049  	}
  1050  
  1051  	sharingCollaborationClient, err := s.sharingCollaborationSelector.Next()
  1052  	if err != nil {
  1053  		return nil, nil, err
  1054  	}
  1055  
  1056  	// we can get the share if the reference carries a share id
  1057  	if ref.ResourceId.OpaqueId != utils.ShareStorageProviderID {
  1058  		// look up share for this resourceid
  1059  		lsRes, err := sharingCollaborationClient.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{
  1060  			Ref: &collaboration.ShareReference{
  1061  				Spec: &collaboration.ShareReference_Id{
  1062  					Id: &collaboration.ShareId{
  1063  						OpaqueId: ref.ResourceId.OpaqueId,
  1064  					},
  1065  				},
  1066  			},
  1067  		})
  1068  
  1069  		if err != nil {
  1070  			return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling GetReceivedShare")
  1071  		}
  1072  		if lsRes.Status.Code != rpc.Code_CODE_OK {
  1073  			return nil, lsRes.Status, nil
  1074  		}
  1075  		if lsRes.Share.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
  1076  			return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil
  1077  		}
  1078  		return lsRes.Share, lsRes.Status, nil
  1079  	}
  1080  
  1081  	// we currently need to list all accepted shares and match the path if the
  1082  	// request is relative to the share jail root. Also we need to Stat() the
  1083  	// shared resource's id to check whether that still exist. There might be
  1084  	// old shares using the same path but for an already vanished resource id.
  1085  	if ref.ResourceId.OpaqueId == utils.ShareStorageProviderID && ref.Path != "." {
  1086  		lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
  1087  			Filters: []*collaboration.Filter{
  1088  				{
  1089  					Type: collaboration.Filter_TYPE_STATE,
  1090  					Term: &collaboration.Filter_State{
  1091  						State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
  1092  					},
  1093  				},
  1094  				// TODO filter by mountpoint?
  1095  			},
  1096  		})
  1097  		if err != nil {
  1098  			return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling GetReceivedShare")
  1099  		}
  1100  		if lsRes.Status.Code != rpc.Code_CODE_OK {
  1101  			return nil, lsRes.Status, nil
  1102  		}
  1103  		for _, receivedShare := range lsRes.Shares {
  1104  			if isMountPointForPath(receivedShare.MountPoint.Path, ref.Path) {
  1105  				// Only return this share if the resource still exists.
  1106  				gatewayClient, err := s.gatewaySelector.Next()
  1107  				if err != nil {
  1108  					return nil, nil, err
  1109  				}
  1110  				sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
  1111  					Ref: &provider.Reference{ResourceId: receivedShare.GetShare().GetResourceId()},
  1112  				})
  1113  				if err != nil {
  1114  					appctx.GetLogger(ctx).Debug().
  1115  						Err(err).
  1116  						Interface("resourceID", receivedShare.GetShare().GetResourceId()).
  1117  						Msg("resolveAcceptedShare: failed to stat shared resource")
  1118  					continue
  1119  				}
  1120  				if sRes.Status.Code != rpc.Code_CODE_OK {
  1121  					appctx.GetLogger(ctx).Debug().
  1122  						Interface("resourceID", receivedShare.GetShare().GetResourceId()).
  1123  						Interface("status", sRes.Status).
  1124  						Msg("resolveAcceptedShare: failed to stat shared resource")
  1125  					continue
  1126  				}
  1127  				return receivedShare, lsRes.Status, nil
  1128  			}
  1129  		}
  1130  	}
  1131  
  1132  	return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil
  1133  }
  1134  
  1135  func isMountPointForPath(mountpoint, path string) bool {
  1136  	requiredSegments := strings.Split(strings.TrimPrefix(mountpoint, "./"), "/")
  1137  	pathSegments := strings.Split(strings.TrimPrefix(path, "./"), "/")
  1138  	for i := range requiredSegments {
  1139  		if pathSegments[i] != requiredSegments[i] {
  1140  			return false
  1141  		}
  1142  	}
  1143  	return true
  1144  }
  1145  
  1146  func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare) error {
  1147  	receivedShare.State = collaboration.ShareState_SHARE_STATE_REJECTED
  1148  	receivedShare.MountPoint = nil
  1149  
  1150  	sharingCollaborationClient, err := s.sharingCollaborationSelector.Next()
  1151  	if err != nil {
  1152  		return err
  1153  	}
  1154  
  1155  	res, err := sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{
  1156  		Share:      receivedShare,
  1157  		UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}},
  1158  	})
  1159  	if err != nil {
  1160  		return err
  1161  	}
  1162  
  1163  	return errtypes.NewErrtypeFromStatus(res.Status)
  1164  }
  1165  
  1166  func (s *service) fetchAcceptedShares(ctx context.Context, opaque *typesv1beta1.Opaque, arbitraryMetadataKeys []string, fieldMask *field_mask.FieldMask) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) {
  1167  	sharingCollaborationClient, err := s.sharingCollaborationSelector.Next()
  1168  	if err != nil {
  1169  		return nil, nil, err
  1170  	}
  1171  
  1172  	lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
  1173  		Filters: []*collaboration.Filter{
  1174  			{
  1175  				Type: collaboration.Filter_TYPE_STATE,
  1176  				Term: &collaboration.Filter_State{
  1177  					State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
  1178  				},
  1179  			},
  1180  		},
  1181  	})
  1182  	if err != nil {
  1183  		return nil, nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest")
  1184  	}
  1185  	if lsRes.Status.Code != rpc.Code_CODE_OK {
  1186  		return nil, nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest")
  1187  	}
  1188  
  1189  	numWorkers := s.maxConcurrency
  1190  	if len(lsRes.Shares) < numWorkers {
  1191  		numWorkers = len(lsRes.Shares)
  1192  	}
  1193  	type res struct {
  1194  		shareid string
  1195  		info    *provider.ResourceInfo
  1196  	}
  1197  	work := make(chan *collaboration.ReceivedShare, len(lsRes.Shares))
  1198  	results := make(chan res, len(lsRes.Shares))
  1199  
  1200  	g, ctx := errgroup.WithContext(ctx)
  1201  
  1202  	// Distribute work
  1203  	g.Go(func() error {
  1204  		defer close(work)
  1205  		for _, share := range lsRes.Shares {
  1206  			select {
  1207  			case work <- share:
  1208  			case <-ctx.Done():
  1209  				return ctx.Err()
  1210  			}
  1211  		}
  1212  		return nil
  1213  	})
  1214  
  1215  	// Spawn workers that'll concurrently work the queue
  1216  	for i := 0; i < numWorkers; i++ {
  1217  		g.Go(func() error {
  1218  			for rs := range work {
  1219  
  1220  				// only stat accepted shares
  1221  				if rs.State != collaboration.ShareState_SHARE_STATE_ACCEPTED {
  1222  					continue
  1223  				}
  1224  				if rs.Share.ResourceId.SpaceId == "" {
  1225  					// convert backwards compatible share id
  1226  					rs.Share.ResourceId.StorageId, rs.Share.ResourceId.SpaceId = storagespace.SplitStorageID(rs.Share.ResourceId.StorageId)
  1227  				}
  1228  
  1229  				gatewayClient, err := s.gatewaySelector.Next()
  1230  				if err != nil {
  1231  					appctx.GetLogger(ctx).Error().
  1232  						Err(err).
  1233  						Interface("resourceID", rs.Share.ResourceId).
  1234  						Msg("ListRecievedShares: failed to select next gateway client")
  1235  					return err
  1236  				}
  1237  				sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
  1238  					Opaque:                opaque,
  1239  					Ref:                   &provider.Reference{ResourceId: rs.Share.ResourceId},
  1240  					ArbitraryMetadataKeys: arbitraryMetadataKeys,
  1241  					FieldMask:             fieldMask,
  1242  				})
  1243  				if err != nil {
  1244  					appctx.GetLogger(ctx).Error().
  1245  						Err(err).
  1246  						Interface("resourceID", rs.Share.ResourceId).
  1247  						Msg("ListRecievedShares: failed to make stat call")
  1248  					return err
  1249  				}
  1250  				if sRes.Status.Code != rpc.Code_CODE_OK {
  1251  					appctx.GetLogger(ctx).Debug().
  1252  						Interface("resourceID", rs.Share.ResourceId).
  1253  						Interface("status", sRes.Status).
  1254  						Msg("ListRecievedShares: failed to stat the resource")
  1255  					continue
  1256  				}
  1257  				select {
  1258  				case results <- res{shareid: rs.Share.Id.OpaqueId, info: sRes.Info}:
  1259  				case <-ctx.Done():
  1260  					return ctx.Err()
  1261  				}
  1262  			}
  1263  			return nil
  1264  		})
  1265  	}
  1266  
  1267  	// Wait for things to settle down, then close results chan
  1268  	go func() {
  1269  		_ = g.Wait() // error is checked later
  1270  		close(results)
  1271  	}()
  1272  
  1273  	// some results might have been skipped, so we cannot preallocate the map
  1274  	shareMetaData := make(map[string]*provider.ResourceInfo)
  1275  	for r := range results {
  1276  		shareMetaData[r.shareid] = r.info
  1277  	}
  1278  
  1279  	if err := g.Wait(); err != nil {
  1280  		return nil, nil, err
  1281  	}
  1282  
  1283  	return lsRes.Shares, shareMetaData, nil
  1284  }
  1285  
  1286  func findEarliestShare(receivedShares []*collaboration.ReceivedShare, shareInfo map[string]*provider.ResourceInfo) (earliestShare *collaboration.Share) {
  1287  	for _, rs := range receivedShares {
  1288  		var hasCurrentMd bool
  1289  		var hasEarliestMd bool
  1290  
  1291  		current := rs.Share
  1292  		// We cannot assume that every share has metadata
  1293  		if current.Id != nil {
  1294  			_, hasCurrentMd = shareInfo[current.Id.OpaqueId]
  1295  		}
  1296  		if earliestShare != nil && earliestShare.Id != nil {
  1297  			_, hasEarliestMd = shareInfo[earliestShare.Id.OpaqueId]
  1298  		}
  1299  
  1300  		switch {
  1301  		case earliestShare == nil && hasCurrentMd:
  1302  			earliestShare = current
  1303  		// ignore if one of the shares has no metadata
  1304  		case !hasEarliestMd || !hasCurrentMd:
  1305  			continue
  1306  		case shareInfo[current.Id.OpaqueId].Mtime.Seconds > shareInfo[earliestShare.Id.OpaqueId].Mtime.Seconds:
  1307  			earliestShare = current
  1308  		case shareInfo[current.Id.OpaqueId].Mtime.Seconds == shareInfo[earliestShare.Id.OpaqueId].Mtime.Seconds &&
  1309  			shareInfo[current.Id.OpaqueId].Mtime.Nanos > shareInfo[earliestShare.Id.OpaqueId].Mtime.Nanos:
  1310  			earliestShare = current
  1311  		}
  1312  	}
  1313  	return earliestShare
  1314  }
  1315  
  1316  func buildReferenceInShare(ref *provider.Reference, s *collaboration.ReceivedShare) *provider.Reference {
  1317  	path := ref.Path
  1318  	if isShareJailRoot(ref.ResourceId) {
  1319  		// we need to cut off the mountpoint from the path in the request reference
  1320  		path = utils.MakeRelativePath(strings.TrimPrefix(strings.TrimPrefix(path, "./"), s.MountPoint.Path))
  1321  	}
  1322  	return &provider.Reference{
  1323  		ResourceId: s.Share.ResourceId,
  1324  		Path:       path,
  1325  	}
  1326  }
  1327  
  1328  // isRename checks if the two references lie in the responsibility of the sharesstorageprovider and if a rename occurs
  1329  func isRename(s, d *provider.Reference) bool {
  1330  	// if the source is a share jail child where the path is .
  1331  	return ((isShareJailChild(s.ResourceId) && s.Path == ".") ||
  1332  		// or if the source is the share jail with a single path segment, e.g. './old'
  1333  		(isShareJailRoot(s.ResourceId) && len(strings.SplitN(s.Path, "/", 3)) == 2)) &&
  1334  		// and if the destination is the share jail a single path segment, e.g. './new'
  1335  		isShareJailRoot(d.ResourceId) && len(strings.SplitN(d.Path, "/", 3)) == 2
  1336  }
  1337  
  1338  func isShareJailChild(id *provider.ResourceId) bool {
  1339  	return id.SpaceId == utils.ShareStorageSpaceID && id.OpaqueId != utils.ShareStorageSpaceID
  1340  }
  1341  
  1342  func isShareJailRoot(id *provider.ResourceId) bool {
  1343  	return id.SpaceId == utils.ShareStorageSpaceID && id.OpaqueId == utils.ShareStorageSpaceID
  1344  }
  1345  
  1346  func isVirtualRoot(ref *provider.Reference) bool {
  1347  	return isShareJailRoot(ref.ResourceId) && (ref.Path == "" || ref.Path == "." || ref.Path == "./")
  1348  }