github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/gateway/usershareprovider.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 gateway
    20  
    21  import (
    22  	"context"
    23  	"slices"
    24  
    25  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    26  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    27  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    28  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    29  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    30  	"github.com/cs3org/reva/v2/pkg/appctx"
    31  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    34  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    35  	"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
    36  	"github.com/cs3org/reva/v2/pkg/utils"
    37  	"github.com/pkg/errors"
    38  )
    39  
    40  // TODO(labkode): add multi-phase commit logic when commit share or commit ref is enabled.
    41  func (s *svc) CreateShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) {
    42  	// Don't use the share manager when sharing a space root
    43  	if !s.c.UseCommonSpaceRootShareLogic && refIsSpaceRoot(req.ResourceInfo.Id) {
    44  		return s.addSpaceShare(ctx, req)
    45  	}
    46  	return s.addShare(ctx, req)
    47  }
    48  
    49  func (s *svc) RemoveShare(ctx context.Context, req *collaboration.RemoveShareRequest) (*collaboration.RemoveShareResponse, error) {
    50  	key := req.GetRef().GetKey()
    51  	if !s.c.UseCommonSpaceRootShareLogic && shareIsSpaceRoot(key) {
    52  		return s.removeSpaceShare(ctx, key.GetResourceId(), key.GetGrantee())
    53  	}
    54  	return s.removeShare(ctx, req)
    55  }
    56  
    57  func (s *svc) UpdateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) {
    58  	if !s.c.UseCommonSpaceRootShareLogic && refIsSpaceRoot(req.GetShare().GetResourceId()) {
    59  		return s.updateSpaceShare(ctx, req)
    60  	}
    61  	return s.updateShare(ctx, req)
    62  }
    63  
    64  // TODO(labkode): we need to validate share state vs storage grant and storage ref
    65  // If there are any inconsistencies, the share needs to be flag as invalid and a background process
    66  // or active fix needs to be performed.
    67  func (s *svc) GetShare(ctx context.Context, req *collaboration.GetShareRequest) (*collaboration.GetShareResponse, error) {
    68  	return s.getShare(ctx, req)
    69  }
    70  
    71  func (s *svc) getShare(ctx context.Context, req *collaboration.GetShareRequest) (*collaboration.GetShareResponse, error) {
    72  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
    73  	if err != nil {
    74  		appctx.GetLogger(ctx).
    75  			Err(err).
    76  			Msg("getShare: failed to get user share provider")
    77  		return &collaboration.GetShareResponse{
    78  			Status: status.NewInternal(ctx, "error getting user share provider client"),
    79  		}, nil
    80  	}
    81  
    82  	res, err := c.GetShare(ctx, req)
    83  	if err != nil {
    84  		return nil, errors.Wrap(err, "gateway: error calling GetShare")
    85  	}
    86  
    87  	return res, nil
    88  }
    89  
    90  // TODO(labkode): read GetShare comment.
    91  func (s *svc) ListShares(ctx context.Context, req *collaboration.ListSharesRequest) (*collaboration.ListSharesResponse, error) {
    92  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
    93  	if err != nil {
    94  		appctx.GetLogger(ctx).
    95  			Err(err).
    96  			Msg("ListShares: failed to get user share provider")
    97  		return &collaboration.ListSharesResponse{
    98  			Status: status.NewInternal(ctx, "error getting user share provider client"),
    99  		}, nil
   100  	}
   101  
   102  	res, err := c.ListShares(ctx, req)
   103  	if err != nil {
   104  		return nil, errors.Wrap(err, "gateway: error calling ListShares")
   105  	}
   106  
   107  	return res, nil
   108  }
   109  
   110  func (s *svc) ListExistingShares(_ context.Context, _ *collaboration.ListSharesRequest) (*gateway.ListExistingSharesResponse, error) {
   111  	return nil, errtypes.NotSupported("method ListExistingShares not implemented")
   112  }
   113  
   114  func (s *svc) updateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) {
   115  	// TODO: update wopi server
   116  	// FIXME This is a workaround that should prevent removing or changing the share permissions when the file is locked.
   117  	// https://github.com/owncloud/ocis/issues/8474
   118  	if status, err := s.checkLock(ctx, req.GetShare().GetId()); err != nil {
   119  		return &collaboration.UpdateShareResponse{
   120  			Status: status,
   121  		}, nil
   122  	}
   123  
   124  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   125  	if err != nil {
   126  		appctx.GetLogger(ctx).
   127  			Err(err).
   128  			Msg("UpdateShare: failed to get user share provider")
   129  		return &collaboration.UpdateShareResponse{
   130  			Status: status.NewInternal(ctx, "error getting share provider client"),
   131  		}, nil
   132  	}
   133  	res, err := c.UpdateShare(ctx, req)
   134  	if err != nil {
   135  		return nil, errors.Wrap(err, "gateway: error calling UpdateShare")
   136  	}
   137  	if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
   138  		return res, nil
   139  	}
   140  
   141  	if s.c.CommitShareToStorageGrant {
   142  		creator, ok := ctxpkg.ContextGetUser(ctx)
   143  		if !ok {
   144  			return nil, errors.New("user not found in context")
   145  		}
   146  
   147  		grant := &provider.Grant{
   148  			Grantee:     res.GetShare().GetGrantee(),
   149  			Permissions: res.GetShare().GetPermissions().GetPermissions(),
   150  			Expiration:  res.GetShare().GetExpiration(),
   151  			Creator:     creator.GetId(),
   152  		}
   153  		updateGrantStatus, err := s.updateGrant(ctx, res.GetShare().GetResourceId(), grant, nil)
   154  
   155  		if err != nil {
   156  			return nil, errors.Wrap(err, "gateway: error calling updateGrant")
   157  		}
   158  
   159  		if updateGrantStatus.Code != rpc.Code_CODE_OK {
   160  			return &collaboration.UpdateShareResponse{
   161  				Status: updateGrantStatus,
   162  				Share:  res.GetShare(),
   163  			}, nil
   164  		}
   165  	}
   166  
   167  	return res, nil
   168  }
   169  
   170  func (s *svc) updateSpaceShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) {
   171  	if req.GetShare().GetGrantee() == nil {
   172  		return &collaboration.UpdateShareResponse{Status: status.NewInvalid(ctx, "updating requires a received grantee object")}, nil
   173  	}
   174  	// If the share is a denial we call  denyGrant instead.
   175  	var st *rpc.Status
   176  	var err error
   177  	// TODO: change CS3 APIs
   178  	opaque := &typesv1beta1.Opaque{
   179  		Map: map[string]*typesv1beta1.OpaqueEntry{
   180  			"spacegrant": {},
   181  		},
   182  	}
   183  	utils.AppendPlainToOpaque(opaque, "spacetype", utils.ReadPlainFromOpaque(req.Opaque, "spacetype"))
   184  
   185  	if grants.PermissionsEqual(req.Share.GetPermissions().GetPermissions(), &provider.ResourcePermissions{}) {
   186  		st, err = s.denyGrant(ctx, req.GetShare().GetResourceId(), req.GetShare().GetGrantee(), opaque)
   187  		if err != nil {
   188  			return nil, errors.Wrap(err, "gateway: error denying grant in storage")
   189  		}
   190  	} else {
   191  		listGrantRes, err := s.listGrants(ctx, req.GetShare().GetResourceId())
   192  		if err != nil {
   193  			return nil, errors.Wrap(err, "gateway: error getting grant to remove from storage")
   194  		}
   195  		existsGrant := s.getGranteeGrant(listGrantRes.GetGrants(), req.GetShare().GetGrantee())
   196  
   197  		if !slices.Contains(req.GetUpdateMask().GetPaths(), "permissions") {
   198  			req.Share.Permissions = &collaboration.SharePermissions{Permissions: existsGrant.GetPermissions()}
   199  		}
   200  
   201  		if !slices.Contains(req.GetUpdateMask().GetPaths(), "expiration") {
   202  			req.Share.Expiration = existsGrant.GetExpiration()
   203  		}
   204  
   205  		u, ok := ctxpkg.ContextGetUser(ctx)
   206  		if !ok {
   207  			return nil, errors.New("user not found in context")
   208  		}
   209  
   210  		grant := &provider.Grant{
   211  			Grantee:     req.GetShare().GetGrantee(),
   212  			Permissions: req.GetShare().GetPermissions().GetPermissions(),
   213  			Expiration:  req.GetShare().GetExpiration(),
   214  			Creator:     u.GetId(),
   215  		}
   216  
   217  		if grant.GetPermissions() == nil {
   218  			return &collaboration.UpdateShareResponse{Status: status.NewInvalid(ctx, "updating requires a received permission object")}, nil
   219  		}
   220  
   221  		if !grant.GetPermissions().GetRemoveGrant() {
   222  			// this request might remove Manager Permissions so we need to
   223  			// check if there is at least one manager remaining of the
   224  			// resource.
   225  			if !isSpaceManagerRemaining(listGrantRes.GetGrants(), grant.GetGrantee()) {
   226  				return &collaboration.UpdateShareResponse{
   227  					Status: status.NewPermissionDenied(ctx, errtypes.PermissionDenied(""), "can't remove the last manager"),
   228  				}, nil
   229  			}
   230  
   231  		}
   232  		st, err = s.updateGrant(ctx, req.GetShare().GetResourceId(), grant, opaque)
   233  		if err != nil {
   234  			return nil, errors.Wrap(err, "gateway: error adding grant to storage")
   235  		}
   236  	}
   237  
   238  	res := &collaboration.UpdateShareResponse{
   239  		Status: st,
   240  		Share:  req.Share,
   241  	}
   242  
   243  	if st.Code != rpc.Code_CODE_OK {
   244  		return res, nil
   245  	}
   246  
   247  	s.providerCache.RemoveListStorageProviders(req.GetShare().GetResourceId())
   248  	return res, nil
   249  }
   250  
   251  // TODO(labkode): listing received shares just goes to the user share manager and gets the list of
   252  // received shares. The display name of the shares should be the a friendly name, like the basename
   253  // of the original file.
   254  func (s *svc) ListReceivedShares(ctx context.Context, req *collaboration.ListReceivedSharesRequest) (*collaboration.ListReceivedSharesResponse, error) {
   255  	logger := appctx.GetLogger(ctx)
   256  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   257  	if err != nil {
   258  		logger.Error().
   259  			Err(err).
   260  			Msg("ListReceivedShares: failed to get user share provider")
   261  		return &collaboration.ListReceivedSharesResponse{
   262  			Status: status.NewInternal(ctx, "error getting share provider client"),
   263  		}, nil
   264  	}
   265  
   266  	res, err := c.ListReceivedShares(ctx, req)
   267  	if err != nil {
   268  		return nil, errors.Wrap(err, "gateway: error calling ListReceivedShares")
   269  	}
   270  	return res, nil
   271  }
   272  
   273  func (s *svc) GetReceivedShare(ctx context.Context, req *collaboration.GetReceivedShareRequest) (*collaboration.GetReceivedShareResponse, error) {
   274  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   275  	if err != nil {
   276  		appctx.GetLogger(ctx).
   277  			Err(err).
   278  			Msg("GetReceivedShare: failed to get user share provider")
   279  		return &collaboration.GetReceivedShareResponse{
   280  			Status: status.NewInternal(ctx, "error getting received share"),
   281  		}, nil
   282  	}
   283  
   284  	res, err := c.GetReceivedShare(ctx, req)
   285  	if err != nil {
   286  		return nil, errors.Wrap(err, "gateway: error calling GetReceivedShare")
   287  	}
   288  
   289  	return res, nil
   290  }
   291  
   292  // When updating a received share:
   293  // if the update contains update for displayName:
   294  //  1. if received share is mounted: we also do a rename in the storage
   295  //  2. if received share is not mounted: we only rename in user share provider.
   296  func (s *svc) UpdateReceivedShare(ctx context.Context, req *collaboration.UpdateReceivedShareRequest) (*collaboration.UpdateReceivedShareResponse, error) {
   297  	ctx, span := appctx.GetTracerProvider(ctx).Tracer("gateway").Start(ctx, "Gateway.UpdateReceivedShare")
   298  	defer span.End()
   299  
   300  	// sanity checks
   301  	switch {
   302  	case req.GetShare() == nil:
   303  		return &collaboration.UpdateReceivedShareResponse{
   304  			Status: status.NewInvalid(ctx, "updating requires a received share object"),
   305  		}, nil
   306  	case req.GetShare().GetShare() == nil:
   307  		return &collaboration.UpdateReceivedShareResponse{
   308  			Status: status.NewInvalid(ctx, "share missing"),
   309  		}, nil
   310  	case req.GetShare().GetShare().GetId() == nil:
   311  		return &collaboration.UpdateReceivedShareResponse{
   312  			Status: status.NewInvalid(ctx, "share id missing"),
   313  		}, nil
   314  	case req.GetShare().GetShare().GetId().GetOpaqueId() == "":
   315  		return &collaboration.UpdateReceivedShareResponse{
   316  			Status: status.NewInvalid(ctx, "share id empty"),
   317  		}, nil
   318  	}
   319  
   320  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   321  	if err != nil {
   322  		appctx.GetLogger(ctx).
   323  			Err(err).
   324  			Msg("UpdateReceivedShare: failed to get user share provider")
   325  		return &collaboration.UpdateReceivedShareResponse{
   326  			Status: status.NewInternal(ctx, "error getting share provider client"),
   327  		}, nil
   328  	}
   329  
   330  	return c.UpdateReceivedShare(ctx, req)
   331  	/*
   332  		    TODO: Leftover from master merge. Do we need this?
   333  			if err != nil {
   334  				appctx.GetLogger(ctx).
   335  					Err(err).
   336  					Msg("UpdateReceivedShare: failed to get user share provider")
   337  				return &collaboration.UpdateReceivedShareResponse{
   338  					Status: status.NewInternal(ctx, "error getting share provider client"),
   339  				}, nil
   340  			}
   341  			// check if we have a resource id in the update response that we can use to update references
   342  			if res.GetShare().GetShare().GetResourceId() == nil {
   343  				log.Err(err).Msg("gateway: UpdateReceivedShare must return a ResourceId")
   344  				return &collaboration.UpdateReceivedShareResponse{
   345  					Status: &rpc.Status{
   346  						Code: rpc.Code_CODE_INTERNAL,
   347  					},
   348  				}, nil
   349  			}
   350  
   351  			// properties are updated in the order they appear in the field mask
   352  			// when an error occurs the request ends and no further fields are updated
   353  			for i := range req.UpdateMask.Paths {
   354  				switch req.UpdateMask.Paths[i] {
   355  				case "state":
   356  					switch req.GetShare().GetState() {
   357  					case collaboration.ShareState_SHARE_STATE_ACCEPTED:
   358  						rpcStatus := s.createReference(ctx, res.GetShare().GetShare().GetResourceId())
   359  						if rpcStatus.Code != rpc.Code_CODE_OK {
   360  							return &collaboration.UpdateReceivedShareResponse{Status: rpcStatus}, nil
   361  						}
   362  					case collaboration.ShareState_SHARE_STATE_REJECTED:
   363  						rpcStatus := s.removeReference(ctx, res.GetShare().GetShare().ResourceId)
   364  						if rpcStatus.Code != rpc.Code_CODE_OK && rpcStatus.Code != rpc.Code_CODE_NOT_FOUND {
   365  							return &collaboration.UpdateReceivedShareResponse{Status: rpcStatus}, nil
   366  						}
   367  					}
   368  				case "mount_point":
   369  					// TODO(labkode): implementing updating mount point
   370  					err = errtypes.NotSupported("gateway: update of mount point is not yet implemented")
   371  					return &collaboration.UpdateReceivedShareResponse{
   372  						Status: status.NewUnimplemented(ctx, err, "error updating received share"),
   373  					}, nil
   374  				default:
   375  					return nil, errtypes.NotSupported("updating " + req.UpdateMask.Paths[i] + " is not supported")
   376  				}
   377  			}
   378  			return res, nil
   379  	*/
   380  }
   381  
   382  func (s *svc) ListExistingReceivedShares(_ context.Context, _ *collaboration.ListReceivedSharesRequest) (*gateway.ListExistingReceivedSharesResponse, error) {
   383  	return nil, errtypes.NotSupported("Unimplemented")
   384  }
   385  
   386  func (s *svc) denyGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
   387  	ref := &provider.Reference{
   388  		ResourceId: id,
   389  	}
   390  
   391  	grantReq := &provider.DenyGrantRequest{
   392  		Ref:     ref,
   393  		Grantee: g,
   394  		Opaque:  opaque,
   395  		// TODO add creator
   396  	}
   397  
   398  	c, _, err := s.find(ctx, ref)
   399  	if err != nil {
   400  		appctx.GetLogger(ctx).
   401  			Err(err).
   402  			Interface("reference", ref).
   403  			Msg("denyGrant: failed to get storage provider")
   404  		if _, ok := err.(errtypes.IsNotFound); ok {
   405  			return status.NewNotFound(ctx, "storage provider not found"), nil
   406  		}
   407  		return status.NewInternal(ctx, "error finding storage provider"), nil
   408  	}
   409  
   410  	grantRes, err := c.DenyGrant(ctx, grantReq)
   411  	if err != nil {
   412  		return nil, errors.Wrap(err, "gateway: error calling DenyGrant")
   413  	}
   414  	return grantRes.Status, nil
   415  }
   416  
   417  func (s *svc) addGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, expiration *typesv1beta1.Timestamp, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
   418  	ref := &provider.Reference{
   419  		ResourceId: id,
   420  	}
   421  
   422  	creator, ok := ctxpkg.ContextGetUser(ctx)
   423  	if !ok {
   424  		return nil, errors.New("user not found in context")
   425  	}
   426  
   427  	grantReq := &provider.AddGrantRequest{
   428  		Ref: ref,
   429  		Grant: &provider.Grant{
   430  			Grantee:     g,
   431  			Permissions: p,
   432  			Creator:     creator.GetId(),
   433  			Expiration:  expiration,
   434  		},
   435  		Opaque: opaque,
   436  	}
   437  
   438  	c, _, err := s.find(ctx, ref)
   439  	if err != nil {
   440  		appctx.GetLogger(ctx).
   441  			Err(err).
   442  			Interface("reference", ref).
   443  			Msg("addGrant: failed to get storage provider")
   444  		if _, ok := err.(errtypes.IsNotFound); ok {
   445  			return status.NewNotFound(ctx, "storage provider not found"), nil
   446  		}
   447  		return status.NewInternal(ctx, "error finding storage provider"), nil
   448  	}
   449  
   450  	grantRes, err := c.AddGrant(ctx, grantReq)
   451  	if err != nil {
   452  		return nil, errors.Wrap(err, "gateway: error calling AddGrant")
   453  	}
   454  	return grantRes.Status, nil
   455  }
   456  
   457  func (s *svc) updateGrant(ctx context.Context, id *provider.ResourceId, grant *provider.Grant, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
   458  	ref := &provider.Reference{
   459  		ResourceId: id,
   460  	}
   461  
   462  	grantReq := &provider.UpdateGrantRequest{
   463  		Opaque: opaque,
   464  		Ref:    ref,
   465  		Grant:  grant,
   466  	}
   467  
   468  	c, _, err := s.find(ctx, ref)
   469  	if err != nil {
   470  		appctx.GetLogger(ctx).
   471  			Err(err).
   472  			Interface("reference", ref).
   473  			Msg("updateGrant: failed to get storage provider")
   474  		if _, ok := err.(errtypes.IsNotFound); ok {
   475  			return status.NewNotFound(ctx, "storage provider not found"), nil
   476  		}
   477  		return status.NewInternal(ctx, "error finding storage provider"), nil
   478  	}
   479  
   480  	grantRes, err := c.UpdateGrant(ctx, grantReq)
   481  	if err != nil {
   482  		return nil, errors.Wrap(err, "gateway: error calling UpdateGrant")
   483  	}
   484  	if grantRes.Status.Code != rpc.Code_CODE_OK {
   485  		return status.NewInternal(ctx,
   486  			"error committing share to storage grant"), nil
   487  	}
   488  
   489  	return status.NewOK(ctx), nil
   490  }
   491  
   492  func (s *svc) removeGrant(ctx context.Context, id *provider.ResourceId, g *provider.Grantee, p *provider.ResourcePermissions, opaque *typesv1beta1.Opaque) (*rpc.Status, error) {
   493  	ref := &provider.Reference{
   494  		ResourceId: id,
   495  	}
   496  
   497  	grantReq := &provider.RemoveGrantRequest{
   498  		Ref: ref,
   499  		Grant: &provider.Grant{
   500  			Grantee:     g,
   501  			Permissions: p,
   502  		},
   503  		Opaque: opaque,
   504  	}
   505  
   506  	c, _, err := s.find(ctx, ref)
   507  	if err != nil {
   508  		appctx.GetLogger(ctx).
   509  			Err(err).
   510  			Interface("reference", ref).
   511  			Msg("removeGrant: failed to get storage provider")
   512  		if _, ok := err.(errtypes.IsNotFound); ok {
   513  			return status.NewNotFound(ctx, "storage provider not found"), nil
   514  		}
   515  		return status.NewInternal(ctx, "error finding storage provider"), nil
   516  	}
   517  
   518  	grantRes, err := c.RemoveGrant(ctx, grantReq)
   519  	if err != nil {
   520  		return nil, errors.Wrap(err, "gateway: error calling RemoveGrant")
   521  	}
   522  	if grantRes.Status.Code != rpc.Code_CODE_OK {
   523  		return grantRes.GetStatus(), nil
   524  	}
   525  
   526  	return status.NewOK(ctx), nil
   527  }
   528  
   529  func (s *svc) listGrants(ctx context.Context, id *provider.ResourceId) (*provider.ListGrantsResponse, error) {
   530  	ref := &provider.Reference{
   531  		ResourceId: id,
   532  	}
   533  
   534  	grantReq := &provider.ListGrantsRequest{
   535  		Ref: ref,
   536  	}
   537  
   538  	c, _, err := s.find(ctx, ref)
   539  	if err != nil {
   540  		appctx.GetLogger(ctx).
   541  			Err(err).
   542  			Interface("reference", ref).
   543  			Msg("listGrants: failed to get storage provider")
   544  		if _, ok := err.(errtypes.IsNotFound); ok {
   545  			return &provider.ListGrantsResponse{
   546  				Status: status.NewNotFound(ctx, "storage provider not found"),
   547  			}, nil
   548  		}
   549  		return &provider.ListGrantsResponse{
   550  			Status: status.NewInternal(ctx, "error finding storage provider"),
   551  		}, nil
   552  	}
   553  
   554  	grantRes, err := c.ListGrants(ctx, grantReq)
   555  	if err != nil {
   556  		return nil, errors.Wrap(err, "gateway: error calling ListGrants")
   557  	}
   558  	if grantRes.Status.Code != rpc.Code_CODE_OK {
   559  		return &provider.ListGrantsResponse{Status: status.NewInternal(ctx,
   560  				"error listing storage grants"),
   561  			},
   562  			nil
   563  	}
   564  	return grantRes, nil
   565  }
   566  
   567  func (s *svc) getGranteeGrant(grants []*provider.Grant, grantee *provider.Grantee) *provider.Grant {
   568  	for _, g := range grants {
   569  		if isEqualGrantee(g.Grantee, grantee) {
   570  			return g
   571  		}
   572  	}
   573  	return nil
   574  }
   575  
   576  func (s *svc) addShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) {
   577  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   578  	if err != nil {
   579  		appctx.GetLogger(ctx).
   580  			Err(err).
   581  			Msg("CreateShare: failed to get user share provider")
   582  		return &collaboration.CreateShareResponse{
   583  			Status: status.NewInternal(ctx, "error getting user share provider client"),
   584  		}, nil
   585  	}
   586  	// TODO the user share manager needs to be able to decide if the current user is allowed to create that share (and not eg. incerase permissions)
   587  	// jfd: AFAICT this can only be determined by a storage driver - either the storage provider is queried first or the share manager needs to access the storage using a storage driver
   588  	res, err := c.CreateShare(ctx, req)
   589  	if err != nil {
   590  		return nil, errors.Wrap(err, "gateway: error calling CreateShare")
   591  	}
   592  	if res.Status.Code != rpc.Code_CODE_OK {
   593  		return res, nil
   594  	}
   595  
   596  	rollBackFn := func(status *rpc.Status) {
   597  		rmvReq := &collaboration.RemoveShareRequest{
   598  			Ref: &collaboration.ShareReference{
   599  				Spec: &collaboration.ShareReference_Key{
   600  					Key: &collaboration.ShareKey{
   601  						ResourceId: req.ResourceInfo.Id,
   602  						Grantee:    req.Grant.Grantee,
   603  					},
   604  				},
   605  			},
   606  		}
   607  		appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("rollback the CreateShare attempt")
   608  		if resp, err := s.removeShare(ctx, rmvReq); err != nil {
   609  			appctx.GetLogger(ctx).Debug().Interface("status", resp.GetStatus()).Interface("req", req).Msg(err.Error())
   610  		}
   611  	}
   612  
   613  	if s.c.CommitShareToStorageGrant {
   614  		// If the share is a denial we call denyGrant instead.
   615  		var status *rpc.Status
   616  		if grants.PermissionsEqual(req.Grant.Permissions.Permissions, &provider.ResourcePermissions{}) {
   617  			status, err = s.denyGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, nil)
   618  			if err != nil {
   619  				return nil, errors.Wrap(err, "gateway: error denying grant in storage")
   620  			}
   621  		} else {
   622  			status, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, nil)
   623  			if err != nil {
   624  				appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg(err.Error())
   625  				rollBackFn(status)
   626  				return nil, errors.Wrap(err, "gateway: error adding grant to storage")
   627  			}
   628  		}
   629  
   630  		switch status.Code {
   631  		case rpc.Code_CODE_OK:
   632  			// ok
   633  		case rpc.Code_CODE_UNIMPLEMENTED:
   634  			appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("storing grants not supported, ignoring")
   635  			rollBackFn(status)
   636  		default:
   637  			appctx.GetLogger(ctx).Debug().Interface("status", status).Interface("req", req).Msg("storing grants is not successful")
   638  			rollBackFn(status)
   639  			return &collaboration.CreateShareResponse{
   640  				Status: status,
   641  			}, err
   642  		}
   643  	}
   644  	return res, nil
   645  }
   646  
   647  func (s *svc) addSpaceShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) {
   648  	if refIsSpaceRoot(req.GetResourceInfo().GetId()) &&
   649  		(req.GetResourceInfo().GetSpace().GetSpaceType() == _spaceTypePersonal || req.GetResourceInfo().GetSpace().GetSpaceType() == _spaceTypeVirtual) {
   650  		return &collaboration.CreateShareResponse{Status: status.NewInvalid(ctx, "space type is not eligible for sharing")}, nil
   651  	}
   652  	// If the share is a denial we call  denyGrant instead.
   653  	var st *rpc.Status
   654  	var err error
   655  	// TODO: change CS3 APIs
   656  	opaque := &typesv1beta1.Opaque{
   657  		Map: map[string]*typesv1beta1.OpaqueEntry{
   658  			"spacegrant": {},
   659  		},
   660  	}
   661  	utils.AppendPlainToOpaque(
   662  		opaque,
   663  		"spacetype",
   664  		req.ResourceInfo.GetSpace().GetSpaceType(),
   665  	)
   666  	if grants.PermissionsEqual(req.Grant.Permissions.Permissions, &provider.ResourcePermissions{}) {
   667  		st, err = s.denyGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, opaque)
   668  		if err != nil {
   669  			return nil, errors.Wrap(err, "gateway: error denying grant in storage")
   670  		}
   671  	} else {
   672  		st, err = s.addGrant(ctx, req.ResourceInfo.Id, req.Grant.Grantee, req.Grant.Permissions.Permissions, req.Grant.Expiration, opaque)
   673  		if err != nil {
   674  			return nil, errors.Wrap(err, "gateway: error adding grant to storage")
   675  		}
   676  	}
   677  
   678  	switch st.Code {
   679  	case rpc.Code_CODE_OK:
   680  		s.providerCache.RemoveListStorageProviders(req.ResourceInfo.Id)
   681  	case rpc.Code_CODE_UNIMPLEMENTED:
   682  		appctx.GetLogger(ctx).Debug().Interface("status", st).Interface("req", req).Msg("storing grants not supported, ignoring")
   683  	default:
   684  		return &collaboration.CreateShareResponse{
   685  			Status: st,
   686  		}, err
   687  	}
   688  
   689  	return &collaboration.CreateShareResponse{
   690  		Status: status.NewOK(ctx),
   691  		Share: &collaboration.Share{
   692  			ResourceId:  req.ResourceInfo.Id,
   693  			Permissions: &collaboration.SharePermissions{Permissions: req.Grant.Permissions.GetPermissions()},
   694  			Grantee:     req.Grant.Grantee,
   695  		},
   696  	}, nil
   697  }
   698  
   699  func (s *svc) removeShare(ctx context.Context, req *collaboration.RemoveShareRequest) (*collaboration.RemoveShareResponse, error) {
   700  	c, err := pool.GetUserShareProviderClient(s.c.UserShareProviderEndpoint)
   701  	if err != nil {
   702  		appctx.GetLogger(ctx).
   703  			Err(err).
   704  			Msg("RemoveShare: failed to get user share provider")
   705  		return &collaboration.RemoveShareResponse{
   706  			Status: status.NewInternal(ctx, "error getting user share provider client"),
   707  		}, nil
   708  	}
   709  
   710  	// if we need to commit the share, we need the resource it points to.
   711  	var share *collaboration.Share
   712  	// FIXME: I will cause a panic as share will be nil when I'm false
   713  	if s.c.CommitShareToStorageGrant {
   714  		getShareReq := &collaboration.GetShareRequest{
   715  			Ref: req.Ref,
   716  		}
   717  		getShareRes, err := c.GetShare(ctx, getShareReq)
   718  		if err != nil {
   719  			return nil, errors.Wrap(err, "gateway: error calling GetShare")
   720  		}
   721  
   722  		if getShareRes.Status.Code != rpc.Code_CODE_OK {
   723  			res := &collaboration.RemoveShareResponse{
   724  				Status: status.NewInternal(ctx,
   725  					"error getting share when committing to the storage"),
   726  			}
   727  			return res, nil
   728  		}
   729  		share = getShareRes.Share
   730  	}
   731  
   732  	// TODO: update wopi server
   733  	// FIXME This is a workaround that should prevent removing or changing the share permissions when the file is locked.
   734  	// https://github.com/owncloud/ocis/issues/8474
   735  	if status, err := s.checkShareLock(ctx, share); err != nil {
   736  		return &collaboration.RemoveShareResponse{
   737  			Status: status,
   738  		}, nil
   739  	}
   740  
   741  	res, err := c.RemoveShare(ctx, req)
   742  	if err != nil {
   743  		return nil, errors.Wrap(err, "gateway: error calling RemoveShare")
   744  	}
   745  
   746  	if s.c.CommitShareToStorageGrant {
   747  		removeGrantStatus, err := s.removeGrant(ctx, share.ResourceId, share.Grantee, share.Permissions.Permissions, nil)
   748  		if err != nil {
   749  			return nil, errors.Wrap(err, "gateway: error removing grant from storage")
   750  		}
   751  		if removeGrantStatus.Code != rpc.Code_CODE_OK {
   752  			return &collaboration.RemoveShareResponse{
   753  				Status: removeGrantStatus,
   754  			}, err
   755  		}
   756  	}
   757  
   758  	return res, nil
   759  }
   760  
   761  func (s *svc) removeSpaceShare(ctx context.Context, ref *provider.ResourceId, grantee *provider.Grantee) (*collaboration.RemoveShareResponse, error) {
   762  	listGrantRes, err := s.listGrants(ctx, ref)
   763  	if err != nil {
   764  		return nil, errors.Wrap(err, "gateway: error getting grant to remove from storage")
   765  	}
   766  	var permissions *provider.ResourcePermissions
   767  	for _, g := range listGrantRes.Grants {
   768  		if isEqualGrantee(g.Grantee, grantee) {
   769  			permissions = g.Permissions
   770  		}
   771  	}
   772  	if permissions == nil {
   773  		return nil, errors.New("gateway: error getting grant to remove from storage")
   774  	}
   775  
   776  	if len(listGrantRes.Grants) == 1 || !isSpaceManagerRemaining(listGrantRes.Grants, grantee) {
   777  		return &collaboration.RemoveShareResponse{
   778  			Status: status.NewPermissionDenied(ctx, errtypes.PermissionDenied(""), "can't remove the last manager"),
   779  		}, nil
   780  	}
   781  
   782  	// TODO: change CS3 APIs
   783  	opaque := &typesv1beta1.Opaque{
   784  		Map: map[string]*typesv1beta1.OpaqueEntry{
   785  			"spacegrant": {},
   786  		},
   787  	}
   788  	removeGrantStatus, err := s.removeGrant(ctx, ref, grantee, permissions, opaque)
   789  	if err != nil {
   790  		return nil, errors.Wrap(err, "gateway: error removing grant from storage")
   791  	}
   792  	if removeGrantStatus.Code != rpc.Code_CODE_OK {
   793  		return &collaboration.RemoveShareResponse{
   794  			Status: removeGrantStatus,
   795  		}, err
   796  	}
   797  	s.providerCache.RemoveListStorageProviders(ref)
   798  	return &collaboration.RemoveShareResponse{Status: status.NewOK(ctx)}, nil
   799  }
   800  
   801  func isSpaceManagerRemaining(grants []*provider.Grant, grantee *provider.Grantee) bool {
   802  	for _, g := range grants {
   803  		// RemoveGrant is currently the way to check for the manager role
   804  		// If it is not set than the current grant is not for a manager and
   805  		// we can just continue with the next one.
   806  		if g.Permissions.RemoveGrant && !isEqualGrantee(g.Grantee, grantee) {
   807  			return true
   808  		}
   809  	}
   810  	return false
   811  }
   812  
   813  func (s *svc) checkLock(ctx context.Context, shareId *collaboration.ShareId) (*rpc.Status, error) {
   814  	logger := appctx.GetLogger(ctx)
   815  	getShareRes, err := s.GetShare(ctx, &collaboration.GetShareRequest{
   816  		Ref: &collaboration.ShareReference{
   817  			Spec: &collaboration.ShareReference_Id{Id: shareId},
   818  		},
   819  	})
   820  	if err != nil {
   821  		msg := "gateway: error calling GetShare"
   822  		logger.Err(err).Interface("share_id", shareId).Msg(msg)
   823  		return status.NewInternal(ctx, msg), errors.Wrap(err, msg)
   824  	}
   825  	if getShareRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
   826  		msg := "can not get share stat " + getShareRes.GetStatus().GetMessage()
   827  		logger.Debug().Interface("share", shareId).Msg(msg)
   828  		if getShareRes.GetStatus().GetCode() != rpc.Code_CODE_NOT_FOUND {
   829  			return status.NewNotFound(ctx, msg), errors.New(msg)
   830  		}
   831  		return status.NewInternal(ctx, msg), errors.New(msg)
   832  	}
   833  	return s.checkShareLock(ctx, getShareRes.Share)
   834  }
   835  
   836  func (s *svc) checkShareLock(ctx context.Context, share *collaboration.Share) (*rpc.Status, error) {
   837  	logger := appctx.GetLogger(ctx)
   838  	sRes, err := s.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: share.GetResourceId()},
   839  		ArbitraryMetadataKeys: []string{"lockdiscovery"}})
   840  	if err != nil {
   841  		msg := "failed to stat shared resource"
   842  		logger.Err(err).Interface("resource_id", share.GetResourceId()).Msg(msg)
   843  		return status.NewInternal(ctx, msg), errors.Wrap(err, msg)
   844  	}
   845  	if sRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
   846  		msg := "can not get share stat " + sRes.GetStatus().GetMessage()
   847  		logger.Debug().Interface("lock", sRes.GetInfo().GetLock()).Msg(msg)
   848  		if sRes.GetStatus().GetCode() != rpc.Code_CODE_NOT_FOUND {
   849  			return status.NewNotFound(ctx, msg), errors.New(msg)
   850  		}
   851  		return status.NewInternal(ctx, msg), errors.New(msg)
   852  	}
   853  
   854  	if sRes.GetInfo().GetLock() != nil {
   855  		msg := "can not change grants, the shared resource is locked"
   856  		logger.Debug().Interface("lock", sRes.GetInfo().GetLock()).Msg(msg)
   857  		return status.NewLocked(ctx, msg), errors.New(msg)
   858  	}
   859  	return nil, nil
   860  }
   861  
   862  func refIsSpaceRoot(ref *provider.ResourceId) bool {
   863  	if ref == nil {
   864  		return false
   865  	}
   866  	if ref.SpaceId == "" || ref.OpaqueId == "" {
   867  		return false
   868  	}
   869  
   870  	return ref.SpaceId == ref.OpaqueId
   871  }
   872  
   873  func shareIsSpaceRoot(key *collaboration.ShareKey) bool {
   874  	if key == nil {
   875  		return false
   876  	}
   877  	return refIsSpaceRoot(key.ResourceId)
   878  }
   879  
   880  func isEqualGrantee(a, b *provider.Grantee) bool {
   881  	// Ideally we would want to use utils.GranteeEqual()
   882  	// but the grants stored in the decomposedfs aren't complete (missing usertype and idp)
   883  	// because of that the check would fail so we can only check the ... for now.
   884  	if a.Type != b.Type {
   885  		return false
   886  	}
   887  
   888  	var aID, bID string
   889  	switch a.Type {
   890  	case provider.GranteeType_GRANTEE_TYPE_GROUP:
   891  		aID = a.GetGroupId().GetOpaqueId()
   892  		bID = b.GetGroupId().GetOpaqueId()
   893  	case provider.GranteeType_GRANTEE_TYPE_USER:
   894  		aID = a.GetUserId().GetOpaqueId()
   895  		bID = b.GetUserId().GetOpaqueId()
   896  	}
   897  	return aID == bID
   898  }