github.com/cs3org/reva/v2@v2.27.7/pkg/share/manager/cs3/cs3.go (about)

     1  // Copyright 2018-2022 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 cs3
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"net/url"
    26  	"path"
    27  	"strings"
    28  	"sync"
    29  
    30  	gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    31  	groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
    32  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    33  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    34  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    35  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/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/todo/pool"
    40  	"github.com/cs3org/reva/v2/pkg/share"
    41  	"github.com/cs3org/reva/v2/pkg/share/manager/registry"
    42  	"github.com/cs3org/reva/v2/pkg/storage/utils/indexer"
    43  	indexerErrors "github.com/cs3org/reva/v2/pkg/storage/utils/indexer/errors"
    44  	"github.com/cs3org/reva/v2/pkg/storage/utils/indexer/option"
    45  	"github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
    46  	"github.com/cs3org/reva/v2/pkg/storagespace"
    47  	"github.com/cs3org/reva/v2/pkg/utils"
    48  	"github.com/google/uuid"
    49  	"github.com/mitchellh/mapstructure"
    50  	"github.com/pkg/errors"
    51  	"google.golang.org/genproto/protobuf/field_mask"
    52  )
    53  
    54  // Manager implements a share manager using a cs3 storage backend
    55  type Manager struct {
    56  	gatewayClient gatewayv1beta1.GatewayAPIClient
    57  
    58  	sync.RWMutex
    59  	storage metadata.Storage
    60  	indexer indexer.Indexer
    61  
    62  	initialized bool
    63  }
    64  
    65  // ReceivedShareMetadata hold the state information or a received share
    66  type ReceivedShareMetadata struct {
    67  	State      collaboration.ShareState `json:"state"`
    68  	MountPoint *provider.Reference      `json:"mountpoint"`
    69  }
    70  
    71  func init() {
    72  	registry.Register("cs3", NewDefault)
    73  }
    74  
    75  type config struct {
    76  	GatewayAddr       string `mapstructure:"gateway_addr"`
    77  	ProviderAddr      string `mapstructure:"provider_addr"`
    78  	ServiceUserID     string `mapstructure:"service_user_id"`
    79  	ServiceUserIdp    string `mapstructure:"service_user_idp"`
    80  	MachineAuthAPIKey string `mapstructure:"machine_auth_apikey"`
    81  }
    82  
    83  // NewDefault returns a new manager instance with default dependencies
    84  func NewDefault(m map[string]interface{}) (share.Manager, error) {
    85  	c := &config{}
    86  	if err := mapstructure.Decode(m, c); err != nil {
    87  		err = errors.Wrap(err, "error creating a new manager")
    88  		return nil, err
    89  	}
    90  
    91  	s, err := metadata.NewCS3Storage(c.GatewayAddr, c.ProviderAddr, c.ServiceUserID, c.ServiceUserIdp, c.MachineAuthAPIKey)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	indexer := indexer.CreateIndexer(s)
    96  
    97  	client, err := pool.GetGatewayServiceClient(c.GatewayAddr)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	return New(client, s, indexer)
   103  }
   104  
   105  // New returns a new manager instance
   106  func New(gatewayClient gatewayv1beta1.GatewayAPIClient, s metadata.Storage, indexer indexer.Indexer) (*Manager, error) {
   107  	return &Manager{
   108  		gatewayClient: gatewayClient,
   109  		storage:       s,
   110  		indexer:       indexer,
   111  		initialized:   false,
   112  	}, nil
   113  }
   114  
   115  func (m *Manager) initialize() error {
   116  	if m.initialized {
   117  		return nil
   118  	}
   119  
   120  	m.Lock()
   121  	defer m.Unlock()
   122  
   123  	if m.initialized { // check if initialization happened while grabbing the lock
   124  		return nil
   125  	}
   126  
   127  	err := m.storage.Init(context.Background(), "cs3-share-manager-metadata")
   128  	if err != nil {
   129  		return err
   130  	}
   131  	if err := m.storage.MakeDirIfNotExist(context.Background(), "shares"); err != nil {
   132  		return err
   133  	}
   134  	if err := m.storage.MakeDirIfNotExist(context.Background(), "metadata"); err != nil {
   135  		return err
   136  	}
   137  	err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{
   138  		Name: "OwnerId",
   139  		Func: indexOwnerFunc,
   140  	}, "Id.OpaqueId", "shares", "non_unique", nil, true)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{
   145  		Name: "CreatorId",
   146  		Func: indexCreatorFunc,
   147  	}, "Id.OpaqueId", "shares", "non_unique", nil, true)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{
   152  		Name: "GranteeId",
   153  		Func: indexGranteeFunc,
   154  	}, "Id.OpaqueId", "shares", "non_unique", nil, true)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	err = m.indexer.AddIndex(&collaboration.Share{}, option.IndexByFunc{
   159  		Name: "ResourceId",
   160  		Func: indexResourceIDFunc,
   161  	}, "Id.OpaqueId", "shares", "non_unique", nil, true)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	m.initialized = true
   166  	return nil
   167  }
   168  
   169  // Load imports shares and received shares from channels (e.g. during migration)
   170  func (m *Manager) Load(ctx context.Context, shareChan <-chan *collaboration.Share, receivedShareChan <-chan share.ReceivedShareWithUser) error {
   171  	log := appctx.GetLogger(ctx)
   172  	if err := m.initialize(); err != nil {
   173  		return err
   174  	}
   175  
   176  	var mu sync.Mutex
   177  	var wg sync.WaitGroup
   178  	wg.Add(2)
   179  	go func() {
   180  		for s := range shareChan {
   181  			if s == nil {
   182  				continue
   183  			}
   184  			mu.Lock()
   185  			if err := m.persistShare(context.Background(), s); err != nil {
   186  				log.Error().Err(err).Interface("share", s).Msg("error persisting share")
   187  			}
   188  			mu.Unlock()
   189  		}
   190  		wg.Done()
   191  	}()
   192  	go func() {
   193  		for s := range receivedShareChan {
   194  			if s.ReceivedShare != nil && s.UserID != nil {
   195  				mu.Lock()
   196  				if err := m.persistReceivedShare(context.Background(), s.UserID, s.ReceivedShare); err != nil {
   197  					log.Error().Err(err).Interface("received share", s).Msg("error persisting received share")
   198  				}
   199  				mu.Unlock()
   200  			}
   201  		}
   202  		wg.Done()
   203  	}()
   204  	wg.Wait()
   205  
   206  	return nil
   207  }
   208  
   209  func (m *Manager) getMetadata(ctx context.Context, shareid, grantee string) ReceivedShareMetadata {
   210  	// use default values if the grantee didn't configure anything yet
   211  	metadata := ReceivedShareMetadata{
   212  		State: collaboration.ShareState_SHARE_STATE_PENDING,
   213  	}
   214  	data, err := m.storage.SimpleDownload(ctx, path.Join("metadata", shareid, grantee))
   215  	if err != nil {
   216  		return metadata
   217  	}
   218  	err = json.Unmarshal(data, &metadata)
   219  	if err != nil {
   220  		appctx.GetLogger(ctx).Error().Err(err).Str("shareid", shareid).Msg("error fetching share")
   221  	}
   222  	return metadata
   223  }
   224  
   225  // Dump exports shares and received shares to channels (e.g. during migration)
   226  func (m *Manager) Dump(ctx context.Context, shareChan chan<- *collaboration.Share, receivedShareChan chan<- share.ReceivedShareWithUser) error {
   227  	log := appctx.GetLogger(ctx)
   228  	if err := m.initialize(); err != nil {
   229  		return err
   230  	}
   231  
   232  	shareids, err := m.storage.ReadDir(ctx, "shares")
   233  	if err != nil {
   234  		return err
   235  	}
   236  	for _, shareid := range shareids {
   237  		var s *collaboration.Share
   238  		if s, err = m.getShareByID(ctx, shareid); err != nil {
   239  			log.Error().Err(err).Str("shareid", shareid).Msg("error fetching share")
   240  			continue
   241  		}
   242  		// dump share data
   243  		shareChan <- s
   244  		// dump grantee metadata that includes share state and mount path
   245  		grantees, err := m.storage.ReadDir(ctx, path.Join("metadata", s.Id.OpaqueId))
   246  		if err != nil {
   247  			continue
   248  		}
   249  		for _, grantee := range grantees {
   250  			metadata := m.getMetadata(ctx, s.GetId().GetOpaqueId(), grantee)
   251  			g, err := indexToGrantee(grantee)
   252  			if err != nil || g.Type != provider.GranteeType_GRANTEE_TYPE_USER {
   253  				// ignore group grants, as every user has his own received state
   254  				continue
   255  			}
   256  			receivedShareChan <- share.ReceivedShareWithUser{
   257  				UserID: g.GetUserId(),
   258  				ReceivedShare: &collaboration.ReceivedShare{
   259  					Share:      s,
   260  					State:      metadata.State,
   261  					MountPoint: metadata.MountPoint,
   262  				},
   263  			}
   264  		}
   265  	}
   266  	return nil
   267  }
   268  
   269  // Share creates a new share
   270  func (m *Manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) {
   271  	if err := m.initialize(); err != nil {
   272  		return nil, err
   273  	}
   274  	user := ctxpkg.ContextMustGetUser(ctx)
   275  	// do not allow share to myself or the owner if share is for a user
   276  	if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER &&
   277  		(utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) {
   278  		return nil, errtypes.BadRequest("cs3: owner/creator and grantee are the same")
   279  	}
   280  	ts := utils.TSNow()
   281  
   282  	share := &collaboration.Share{
   283  		Id: &collaboration.ShareId{
   284  			OpaqueId: uuid.NewString(),
   285  		},
   286  		ResourceId:  md.Id,
   287  		Permissions: g.Permissions,
   288  		Grantee:     g.Grantee,
   289  		Owner:       md.Owner,
   290  		Creator:     user.Id,
   291  		Ctime:       ts,
   292  		Mtime:       ts,
   293  	}
   294  
   295  	err := m.persistShare(ctx, share)
   296  	return share, err
   297  }
   298  
   299  func (m *Manager) persistShare(ctx context.Context, share *collaboration.Share) error {
   300  	data, err := json.Marshal(share)
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	err = m.storage.SimpleUpload(ctx, shareFilename(share.Id.OpaqueId), data)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	metadataPath := path.Join("metadata", share.Id.OpaqueId)
   311  	err = m.storage.MakeDirIfNotExist(ctx, metadataPath)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	_, err = m.indexer.Add(share)
   317  	if _, ok := err.(*indexerErrors.AlreadyExistsErr); ok {
   318  		return nil
   319  	}
   320  	return err
   321  }
   322  
   323  // GetShare gets the information for a share by the given ref.
   324  func (m *Manager) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) {
   325  	err := m.initialize()
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	var s *collaboration.Share
   331  	switch {
   332  	case ref.GetId() != nil:
   333  		s, err = m.getShareByID(ctx, ref.GetId().OpaqueId)
   334  	case ref.GetKey() != nil:
   335  		s, err = m.getShareByKey(ctx, ref.GetKey())
   336  	default:
   337  		return nil, errtypes.BadRequest("neither share id nor key was given")
   338  	}
   339  
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	// check if we are the owner or the grantee
   345  	user := ctxpkg.ContextMustGetUser(ctx)
   346  	if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE || share.IsCreatedByUser(s, user) || share.IsGrantedToUser(s, user) {
   347  		return s, nil
   348  	}
   349  
   350  	return nil, errtypes.NotFound("not found")
   351  }
   352  
   353  // Unshare deletes the share pointed by ref.
   354  func (m *Manager) Unshare(ctx context.Context, ref *collaboration.ShareReference) error {
   355  	if err := m.initialize(); err != nil {
   356  		return err
   357  	}
   358  	share, err := m.GetShare(ctx, ref)
   359  	if err != nil {
   360  		return err
   361  	}
   362  
   363  	err = m.storage.Delete(ctx, shareFilename(ref.GetId().OpaqueId))
   364  	if err != nil {
   365  		if _, ok := err.(errtypes.NotFound); !ok {
   366  			return err
   367  		}
   368  	}
   369  
   370  	return m.indexer.Delete(share)
   371  }
   372  
   373  // ListShares returns the shares created by the user
   374  func (m *Manager) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) {
   375  	if err := m.initialize(); err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	user, ok := ctxpkg.ContextGetUser(ctx)
   380  	if !ok {
   381  		return nil, errtypes.UserRequired("error getting user from context")
   382  	}
   383  	var rIDs []*provider.ResourceId
   384  	if len(filters) != 0 {
   385  		grouped := share.GroupFiltersByType(filters)
   386  		for _, g := range grouped {
   387  			for _, f := range g {
   388  				if f.GetResourceId() != nil {
   389  					rIDs = append(rIDs, f.GetResourceId())
   390  				}
   391  			}
   392  		}
   393  	}
   394  	var (
   395  		createdShareIds []string
   396  		err             error
   397  	)
   398  	// in spaces, always use the resourceId
   399  	// We could have more than one resourceID
   400  	// which would form a logical OR
   401  	if len(rIDs) != 0 {
   402  		for _, rID := range rIDs {
   403  			shareIDs, err := m.indexer.FindBy(&collaboration.Share{},
   404  				indexer.NewField("ResourceId", resourceIDToIndex(rID)),
   405  			)
   406  			if err != nil {
   407  				return nil, err
   408  			}
   409  			createdShareIds = append(createdShareIds, shareIDs...)
   410  		}
   411  	} else {
   412  		createdShareIds, err = m.indexer.FindBy(&collaboration.Share{},
   413  			indexer.NewField("OwnerId", userIDToIndex(user.Id)),
   414  			indexer.NewField("CreatorId", userIDToIndex(user.Id)),
   415  		)
   416  		if err != nil {
   417  			return nil, err
   418  		}
   419  	}
   420  
   421  	// We use shareMem as a temporary lookup store to check which shares were
   422  	// already added. This is to prevent duplicates.
   423  	shareMem := make(map[string]struct{})
   424  	result := []*collaboration.Share{}
   425  	for _, id := range createdShareIds {
   426  		s, err := m.getShareByID(ctx, id)
   427  		if err != nil {
   428  			return nil, err
   429  		}
   430  		if share.MatchesFilters(s, filters) {
   431  			result = append(result, s)
   432  			shareMem[s.Id.OpaqueId] = struct{}{}
   433  		}
   434  	}
   435  
   436  	// If a user requests to list shares which have not been created by them
   437  	// we have to explicitly fetch these shares and check if the user is
   438  	// allowed to list the shares.
   439  	// Only then can we add these shares to the result.
   440  	grouped := share.GroupFiltersByType(filters)
   441  	idFilter, ok := grouped[collaboration.Filter_TYPE_RESOURCE_ID]
   442  	if !ok {
   443  		return result, nil
   444  	}
   445  
   446  	shareIDsByResourceID := make(map[string]*provider.ResourceId)
   447  	for _, filter := range idFilter {
   448  		resourceID := filter.GetResourceId()
   449  		shareIDs, err := m.indexer.FindBy(&collaboration.Share{},
   450  			indexer.NewField("ResourceId", resourceIDToIndex(resourceID)),
   451  		)
   452  		if err != nil {
   453  			continue
   454  		}
   455  		for _, shareID := range shareIDs {
   456  			shareIDsByResourceID[shareID] = resourceID
   457  		}
   458  	}
   459  
   460  	// statMem is used as a local cache to prevent statting resources which
   461  	// already have been checked.
   462  	statMem := make(map[string]struct{})
   463  	for shareID, resourceID := range shareIDsByResourceID {
   464  		if _, handled := shareMem[shareID]; handled {
   465  			// We don't want to add a share multiple times when we added it
   466  			// already.
   467  			continue
   468  		}
   469  
   470  		if _, checked := statMem[resourceIDToIndex(resourceID)]; !checked {
   471  			sReq := &provider.StatRequest{
   472  				Ref: &provider.Reference{ResourceId: resourceID},
   473  			}
   474  			sRes, err := m.gatewayClient.Stat(ctx, sReq)
   475  			if err != nil {
   476  				continue
   477  			}
   478  			if sRes.Status.Code != rpcv1beta1.Code_CODE_OK {
   479  				continue
   480  			}
   481  			if !sRes.Info.PermissionSet.ListGrants {
   482  				continue
   483  			}
   484  			statMem[resourceIDToIndex(resourceID)] = struct{}{}
   485  		}
   486  
   487  		s, err := m.getShareByID(ctx, shareID)
   488  		if err != nil {
   489  			return nil, err
   490  		}
   491  		if share.MatchesFilters(s, filters) {
   492  			result = append(result, s)
   493  			shareMem[s.Id.OpaqueId] = struct{}{}
   494  		}
   495  	}
   496  
   497  	return result, nil
   498  }
   499  
   500  // UpdateShare updates the mode of the given share.
   501  func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
   502  	if err := m.initialize(); err != nil {
   503  		return nil, err
   504  	}
   505  	share, err := m.GetShare(ctx, ref)
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	share.Permissions = p
   510  
   511  	data, err := json.Marshal(share)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	err = m.storage.SimpleUpload(ctx, shareFilename(share.Id.OpaqueId), data)
   517  
   518  	return share, err
   519  }
   520  
   521  // ListReceivedShares returns the list of shares the user has access to.
   522  func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) {
   523  	if err := m.initialize(); err != nil {
   524  		return nil, err
   525  	}
   526  
   527  	user, ok := ctxpkg.ContextGetUser(ctx)
   528  	if !ok {
   529  		return nil, errtypes.UserRequired("error getting user from context")
   530  	}
   531  
   532  	uid, groups := user.GetId(), user.GetGroups()
   533  	if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
   534  		u, err := utils.GetUser(forUser, m.gatewayClient)
   535  		if err != nil {
   536  			return nil, errtypes.BadRequest("user not found")
   537  		}
   538  		uid = forUser
   539  		groups = u.GetGroups()
   540  	}
   541  	result := []*collaboration.ReceivedShare{}
   542  
   543  	ids, err := granteeToIndex(&provider.Grantee{
   544  		Type: provider.GranteeType_GRANTEE_TYPE_USER,
   545  		Id:   &provider.Grantee_UserId{UserId: uid},
   546  	})
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	receivedIds, err := m.indexer.FindBy(&collaboration.Share{},
   551  		indexer.NewField("GranteeId", ids),
   552  	)
   553  	if err != nil {
   554  		return nil, err
   555  	}
   556  	for _, group := range groups {
   557  		index, err := granteeToIndex(&provider.Grantee{
   558  			Type: provider.GranteeType_GRANTEE_TYPE_GROUP,
   559  			Id:   &provider.Grantee_GroupId{GroupId: &groupv1beta1.GroupId{OpaqueId: group}},
   560  		})
   561  		if err != nil {
   562  			return nil, err
   563  		}
   564  		groupIds, err := m.indexer.FindBy(&collaboration.Share{},
   565  			indexer.NewField("GranteeId", index),
   566  		)
   567  		if err != nil {
   568  			return nil, err
   569  		}
   570  		receivedIds = append(receivedIds, groupIds...)
   571  	}
   572  
   573  	for _, id := range receivedIds {
   574  		s, err := m.getShareByID(ctx, id)
   575  		if err != nil {
   576  			return nil, err
   577  		}
   578  		if !share.MatchesFilters(s, filters) {
   579  			continue
   580  		}
   581  		metadata, err := m.downloadMetadata(ctx, s)
   582  		if err != nil {
   583  			if _, ok := err.(errtypes.NotFound); !ok {
   584  				return nil, err
   585  			}
   586  			// use default values if the grantee didn't configure anything yet
   587  			metadata = ReceivedShareMetadata{
   588  				State: collaboration.ShareState_SHARE_STATE_PENDING,
   589  			}
   590  		}
   591  		result = append(result, &collaboration.ReceivedShare{
   592  			Share:      s,
   593  			State:      metadata.State,
   594  			MountPoint: metadata.MountPoint,
   595  		})
   596  	}
   597  	return result, nil
   598  }
   599  
   600  // GetReceivedShare returns the information for a received share.
   601  func (m *Manager) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) {
   602  	if err := m.initialize(); err != nil {
   603  		return nil, err
   604  	}
   605  
   606  	share, err := m.GetShare(ctx, ref)
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  
   611  	metadata, err := m.downloadMetadata(ctx, share)
   612  	if err != nil {
   613  		if _, ok := err.(errtypes.NotFound); !ok {
   614  			return nil, err
   615  		}
   616  		// use default values if the grantee didn't configure anything yet
   617  		metadata = ReceivedShareMetadata{
   618  			State: collaboration.ShareState_SHARE_STATE_PENDING,
   619  		}
   620  	}
   621  	return &collaboration.ReceivedShare{
   622  		Share:      share,
   623  		State:      metadata.State,
   624  		MountPoint: metadata.MountPoint,
   625  	}, nil
   626  }
   627  
   628  // UpdateReceivedShare updates the received share with share state.
   629  func (m *Manager) UpdateReceivedShare(ctx context.Context, rshare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userpb.UserId) (*collaboration.ReceivedShare, error) {
   630  	if err := m.initialize(); err != nil {
   631  		return nil, err
   632  	}
   633  
   634  	user, ok := ctxpkg.ContextGetUser(ctx)
   635  	if !ok {
   636  		return nil, errtypes.UserRequired("error getting user from context")
   637  	}
   638  
   639  	rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: rshare.Share.Id}})
   640  	if err != nil {
   641  		return nil, err
   642  	}
   643  
   644  	for i := range fieldMask.Paths {
   645  		switch fieldMask.Paths[i] {
   646  		case "state":
   647  			rs.State = rshare.State
   648  		case "mount_point":
   649  			rs.MountPoint = rshare.MountPoint
   650  		case "hidden":
   651  			continue
   652  		default:
   653  			return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported")
   654  		}
   655  	}
   656  
   657  	uid := user.GetId()
   658  	if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
   659  		uid = forUser
   660  	}
   661  
   662  	err = m.persistReceivedShare(ctx, uid, rs)
   663  	if err != nil {
   664  		return nil, err
   665  	}
   666  	return rs, nil
   667  }
   668  
   669  func (m *Manager) persistReceivedShare(ctx context.Context, userID *userpb.UserId, rs *collaboration.ReceivedShare) error {
   670  	err := m.persistShare(ctx, rs.Share)
   671  	if err != nil {
   672  		return err
   673  	}
   674  
   675  	meta := ReceivedShareMetadata{
   676  		State:      rs.State,
   677  		MountPoint: rs.MountPoint,
   678  	}
   679  	data, err := json.Marshal(meta)
   680  	if err != nil {
   681  		return err
   682  	}
   683  
   684  	fn, err := metadataFilename(rs.Share, userID)
   685  	if err != nil {
   686  		return err
   687  	}
   688  	return m.storage.SimpleUpload(ctx, fn, data)
   689  }
   690  
   691  func (m *Manager) downloadMetadata(ctx context.Context, share *collaboration.Share) (ReceivedShareMetadata, error) {
   692  	user, ok := ctxpkg.ContextGetUser(ctx)
   693  	if !ok {
   694  		return ReceivedShareMetadata{}, errtypes.UserRequired("error getting user from context")
   695  	}
   696  
   697  	metadataFn, err := metadataFilename(share, user.Id)
   698  	if err != nil {
   699  		return ReceivedShareMetadata{}, err
   700  	}
   701  	data, err := m.storage.SimpleDownload(ctx, metadataFn)
   702  	if err != nil {
   703  		return ReceivedShareMetadata{}, err
   704  	}
   705  	metadata := ReceivedShareMetadata{}
   706  	err = json.Unmarshal(data, &metadata)
   707  	return metadata, err
   708  }
   709  
   710  func (m *Manager) getShareByID(ctx context.Context, id string) (*collaboration.Share, error) {
   711  	data, err := m.storage.SimpleDownload(ctx, shareFilename(id))
   712  	if err != nil {
   713  		return nil, err
   714  	}
   715  
   716  	userShare := &collaboration.Share{
   717  		Grantee: &provider.Grantee{Id: &provider.Grantee_UserId{}},
   718  	}
   719  	err = json.Unmarshal(data, userShare)
   720  	if err == nil && userShare.Grantee.GetUserId() != nil {
   721  		userShare.ResourceId = storagespace.UpdateLegacyResourceID(userShare.GetResourceId())
   722  		return userShare, nil
   723  	}
   724  
   725  	groupShare := &collaboration.Share{
   726  		Grantee: &provider.Grantee{Id: &provider.Grantee_GroupId{}},
   727  	}
   728  	err = json.Unmarshal(data, groupShare) // try to unmarshal to a group share if the user share unmarshalling failed
   729  	if err == nil && groupShare.Grantee.GetGroupId() != nil {
   730  		groupShare.ResourceId = storagespace.UpdateLegacyResourceID(groupShare.GetResourceId())
   731  		return groupShare, nil
   732  	}
   733  
   734  	return nil, errtypes.InternalError("failed to unmarshal share data")
   735  }
   736  
   737  func (m *Manager) getShareByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) {
   738  	ownerIds, err := m.indexer.FindBy(&collaboration.Share{},
   739  		indexer.NewField("OwnerId", userIDToIndex(key.Owner)),
   740  	)
   741  	if err != nil {
   742  		return nil, err
   743  	}
   744  	granteeIndex, err := granteeToIndex(key.Grantee)
   745  	if err != nil {
   746  		return nil, err
   747  	}
   748  	granteeIds, err := m.indexer.FindBy(&collaboration.Share{},
   749  		indexer.NewField("GranteeId", granteeIndex),
   750  	)
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  
   755  	ids := intersectSlices(ownerIds, granteeIds)
   756  	for _, id := range ids {
   757  		share, err := m.getShareByID(ctx, id)
   758  		if err != nil {
   759  			return nil, err
   760  		}
   761  		if utils.ResourceIDEqual(share.ResourceId, key.ResourceId) {
   762  			return share, nil
   763  		}
   764  	}
   765  	return nil, errtypes.NotFound("share not found")
   766  }
   767  
   768  func shareFilename(id string) string {
   769  	return path.Join("shares", id)
   770  }
   771  
   772  func metadataFilename(s *collaboration.Share, g interface{}) (string, error) {
   773  	var granteePart string
   774  	switch v := g.(type) {
   775  	case *userpb.UserId:
   776  		granteePart = url.QueryEscape("user:" + v.Idp + ":" + v.OpaqueId)
   777  	case *provider.Grantee:
   778  		var err error
   779  		granteePart, err = granteeToIndex(v)
   780  		if err != nil {
   781  			return "", err
   782  		}
   783  	}
   784  	return path.Join("metadata", s.Id.OpaqueId, granteePart), nil
   785  }
   786  
   787  func indexOwnerFunc(v interface{}) (string, error) {
   788  	share, ok := v.(*collaboration.Share)
   789  	if !ok {
   790  		return "", fmt.Errorf("given entity is not a share")
   791  	}
   792  	return userIDToIndex(share.Owner), nil
   793  }
   794  
   795  func indexCreatorFunc(v interface{}) (string, error) {
   796  	share, ok := v.(*collaboration.Share)
   797  	if !ok {
   798  		return "", fmt.Errorf("given entity is not a share")
   799  	}
   800  	return userIDToIndex(share.Creator), nil
   801  }
   802  
   803  func userIDToIndex(id *userpb.UserId) string {
   804  	return url.QueryEscape(id.Idp + ":" + id.OpaqueId)
   805  }
   806  
   807  func indexGranteeFunc(v interface{}) (string, error) {
   808  	share, ok := v.(*collaboration.Share)
   809  	if !ok {
   810  		return "", fmt.Errorf("given entity is not a share")
   811  	}
   812  	return granteeToIndex(share.Grantee)
   813  }
   814  
   815  func indexResourceIDFunc(v interface{}) (string, error) {
   816  	share, ok := v.(*collaboration.Share)
   817  	if !ok {
   818  		return "", fmt.Errorf("given entity is not a share")
   819  	}
   820  	return resourceIDToIndex(share.ResourceId), nil
   821  }
   822  
   823  func resourceIDToIndex(id *provider.ResourceId) string {
   824  	return strings.Join([]string{id.SpaceId, id.OpaqueId}, "!")
   825  }
   826  
   827  func granteeToIndex(grantee *provider.Grantee) (string, error) {
   828  	switch {
   829  	case grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER:
   830  		return url.QueryEscape("user:" + grantee.GetUserId().Idp + ":" + grantee.GetUserId().OpaqueId), nil
   831  	case grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP:
   832  		return url.QueryEscape("group:" + grantee.GetGroupId().OpaqueId), nil
   833  	default:
   834  		return "", fmt.Errorf("unknown grantee type")
   835  	}
   836  }
   837  
   838  // indexToGrantee tries to unparse a grantee in a metadata dir
   839  // unfortunately, it is just concatenated by :, causing nasty corner cases
   840  func indexToGrantee(name string) (*provider.Grantee, error) {
   841  	unescaped, err := url.QueryUnescape(name)
   842  	if err != nil {
   843  		return nil, err
   844  	}
   845  	parts := strings.SplitN(unescaped, ":", 2)
   846  	if len(parts) != 2 {
   847  		return nil, fmt.Errorf("invalid grantee %s", unescaped)
   848  	}
   849  	switch parts[0] {
   850  	case "user":
   851  		lastInd := strings.LastIndex(parts[1], ":")
   852  		return &provider.Grantee{
   853  			Type: provider.GranteeType_GRANTEE_TYPE_USER,
   854  			Id: &provider.Grantee_UserId{
   855  				UserId: &userpb.UserId{
   856  					Idp:      parts[1][:lastInd],
   857  					OpaqueId: parts[1][lastInd+1:],
   858  				},
   859  			},
   860  		}, nil
   861  	case "group":
   862  		return &provider.Grantee{
   863  			Type: provider.GranteeType_GRANTEE_TYPE_GROUP,
   864  			Id: &provider.Grantee_GroupId{
   865  				GroupId: &groupv1beta1.GroupId{
   866  					OpaqueId: parts[1],
   867  				},
   868  			},
   869  		}, nil
   870  	default:
   871  		return nil, fmt.Errorf("invalid grantee %s", unescaped)
   872  	}
   873  }
   874  
   875  func intersectSlices(a, b []string) []string {
   876  	aMap := map[string]bool{}
   877  	for _, s := range a {
   878  		aMap[s] = true
   879  	}
   880  	result := []string{}
   881  	for _, s := range b {
   882  		if _, ok := aMap[s]; ok {
   883  			result = append(result, s)
   884  		}
   885  	}
   886  	return result
   887  }