github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/usershareprovider/usershareprovider_test.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 usershareprovider_test 20 21 import ( 22 "context" 23 "path/filepath" 24 "regexp" 25 26 gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" 27 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 28 permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1" 29 rpcpb "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 30 collaborationpb "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 31 providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 32 . "github.com/onsi/ginkgo/v2" 33 . "github.com/onsi/gomega" 34 "github.com/pkg/errors" 35 "github.com/stretchr/testify/mock" 36 "google.golang.org/grpc" 37 "google.golang.org/protobuf/types/known/fieldmaskpb" 38 39 "github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider" 40 "github.com/cs3org/reva/v2/pkg/conversions" 41 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 42 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 43 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 44 "github.com/cs3org/reva/v2/pkg/share" 45 _ "github.com/cs3org/reva/v2/pkg/share/manager/loader" 46 "github.com/cs3org/reva/v2/pkg/share/manager/registry" 47 "github.com/cs3org/reva/v2/pkg/share/mocks" 48 "github.com/cs3org/reva/v2/pkg/utils" 49 cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" 50 ) 51 52 var _ = Describe("user share provider service", func() { 53 var ( 54 ctx context.Context 55 provider collaborationpb.CollaborationAPIServer 56 manager *mocks.Manager 57 gatewayClient *cs3mocks.GatewayAPIClient 58 gatewaySelector pool.Selectable[gateway.GatewayAPIClient] 59 checkPermissionResponse *permissions.CheckPermissionResponse 60 statResourceResponse *providerpb.StatResponse 61 cs3permissionsNoAddGrant *providerpb.ResourcePermissions 62 getShareResponse *collaborationpb.Share 63 ) 64 cs3permissionsNoAddGrant = conversions.RoleFromName("manager").CS3ResourcePermissions() 65 cs3permissionsNoAddGrant.AddGrant = false 66 67 BeforeEach(func() { 68 manager = &mocks.Manager{} 69 70 registry.Register("mockManager", func(m map[string]interface{}) (share.Manager, error) { 71 return manager, nil 72 }) 73 manager.On("UpdateShare", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&collaborationpb.Share{}, nil) 74 75 gatewayClient = &cs3mocks.GatewayAPIClient{} 76 pool.RemoveSelector("GatewaySelector" + "any") 77 gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( 78 "GatewaySelector", 79 "any", 80 func(cc grpc.ClientConnInterface) gateway.GatewayAPIClient { 81 return gatewayClient 82 }, 83 ) 84 checkPermissionResponse = &permissions.CheckPermissionResponse{ 85 Status: status.NewOK(ctx), 86 } 87 gatewayClient.On("CheckPermission", mock.Anything, mock.Anything). 88 Return(checkPermissionResponse, nil) 89 90 statResourceResponse = &providerpb.StatResponse{ 91 Status: status.NewOK(ctx), 92 Info: &providerpb.ResourceInfo{ 93 PermissionSet: &providerpb.ResourcePermissions{}, 94 }, 95 } 96 gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResourceResponse, nil) 97 alice := &userpb.User{ 98 Id: &userpb.UserId{ 99 OpaqueId: "alice", 100 }, 101 Username: "alice", 102 } 103 104 getShareResponse = &collaborationpb.Share{ 105 Id: &collaborationpb.ShareId{ 106 OpaqueId: "shareid", 107 }, 108 ResourceId: &providerpb.ResourceId{ 109 StorageId: "storageid", 110 SpaceId: "spaceid", 111 OpaqueId: "opaqueid", 112 }, 113 Owner: alice.Id, 114 Creator: alice.Id, 115 } 116 manager.On("GetShare", mock.Anything, mock.Anything).Return(getShareResponse, nil) 117 118 rgrpcService := usershareprovider.New(gatewaySelector, manager, []*regexp.Regexp{}) 119 120 provider = rgrpcService.(collaborationpb.CollaborationAPIServer) 121 Expect(provider).ToNot(BeNil()) 122 123 ctx = ctxpkg.ContextSetUser(context.Background(), alice) 124 }) 125 126 Describe("UpdateReceivedShare", func() { 127 DescribeTable("validates the share update request", 128 func( 129 req *collaborationpb.UpdateReceivedShareRequest, 130 expectedStatus *rpcpb.Status, 131 expectedError error, 132 ) { 133 134 res, err := provider.UpdateReceivedShare(ctx, req) 135 136 switch expectedError { 137 case nil: 138 Expect(err).To(BeNil()) 139 } 140 141 Expect(res.GetStatus().GetCode()).To(Equal(expectedStatus.GetCode())) 142 Expect(res.GetStatus().GetMessage()).To(ContainSubstring(expectedStatus.GetMessage())) 143 }, 144 Entry( 145 "no share opaque id", 146 &collaborationpb.UpdateReceivedShareRequest{ 147 Share: &collaborationpb.ReceivedShare{ 148 Share: &collaborationpb.Share{ 149 Id: &collaborationpb.ShareId{}, 150 }, 151 }, 152 }, 153 status.NewInvalid(ctx, "share id empty"), 154 nil, 155 ), 156 ) 157 158 DescribeTable("fails if getting the share fails", 159 func( 160 req *collaborationpb.UpdateReceivedShareRequest, 161 expectedStatus *rpcpb.Status, 162 expectedError error, 163 ) { 164 gatewayClient.EXPECT(). 165 GetReceivedShare(mock.Anything, mock.Anything, mock.Anything). 166 RunAndReturn(func(ctx context.Context, request *collaborationpb.GetReceivedShareRequest, option ...grpc.CallOption) (*collaborationpb.GetReceivedShareResponse, error) { 167 return &collaborationpb.GetReceivedShareResponse{ 168 Status: expectedStatus, 169 }, expectedError 170 }) 171 172 res, err := provider.UpdateReceivedShare(ctx, req) 173 174 switch expectedError { 175 case nil: 176 Expect(err).To(BeNil()) 177 default: 178 Expect(err).To(MatchError(expectedError)) 179 } 180 181 switch expectedStatus { 182 case nil: 183 Expect(expectedStatus).To(BeNil()) 184 default: 185 Expect(res.GetStatus().GetCode()).To(Equal(expectedStatus.GetCode())) 186 Expect(res.GetStatus().GetMessage()).To(ContainSubstring(expectedStatus.GetMessage())) 187 } 188 }, 189 Entry( 190 "requesting the share errors", 191 &collaborationpb.UpdateReceivedShareRequest{ 192 UpdateMask: &fieldmaskpb.FieldMask{ 193 Paths: []string{"state"}, 194 }, 195 Share: &collaborationpb.ReceivedShare{ 196 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 197 Share: &collaborationpb.Share{ 198 Id: &collaborationpb.ShareId{ 199 OpaqueId: "1", 200 }, 201 }, 202 }, 203 }, 204 nil, 205 errors.New("some"), 206 ), 207 Entry( 208 "requesting the share fails", 209 &collaborationpb.UpdateReceivedShareRequest{ 210 UpdateMask: &fieldmaskpb.FieldMask{ 211 Paths: []string{"state"}, 212 }, 213 Share: &collaborationpb.ReceivedShare{ 214 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 215 Share: &collaborationpb.Share{ 216 Id: &collaborationpb.ShareId{ 217 OpaqueId: "1", 218 }, 219 }, 220 }, 221 }, 222 status.NewInvalid(ctx, "something"), 223 nil, 224 ), 225 ) 226 227 DescribeTable("fails if the resource stat fails", 228 func( 229 req *collaborationpb.UpdateReceivedShareRequest, 230 expectedStatus *rpcpb.Status, 231 expectedError error, 232 ) { 233 gatewayClient.EXPECT(). 234 GetReceivedShare(mock.Anything, mock.Anything, mock.Anything). 235 RunAndReturn(func(ctx context.Context, request *collaborationpb.GetReceivedShareRequest, option ...grpc.CallOption) (*collaborationpb.GetReceivedShareResponse, error) { 236 return &collaborationpb.GetReceivedShareResponse{ 237 Status: status.NewOK(ctx), 238 }, nil 239 }) 240 gatewayClient.EXPECT().Stat(mock.Anything, mock.Anything, mock.Anything).Unset() 241 gatewayClient.EXPECT(). 242 Stat(mock.Anything, mock.Anything, mock.Anything). 243 RunAndReturn(func(ctx context.Context, request *providerpb.StatRequest, option ...grpc.CallOption) (*providerpb.StatResponse, error) { 244 return &providerpb.StatResponse{ 245 Status: expectedStatus, 246 }, expectedError 247 }) 248 249 res, err := provider.UpdateReceivedShare(ctx, req) 250 251 switch expectedError { 252 case nil: 253 Expect(err).To(BeNil()) 254 default: 255 Expect(err).To(MatchError(expectedError)) 256 } 257 258 switch expectedStatus { 259 case nil: 260 Expect(expectedStatus).To(BeNil()) 261 default: 262 Expect(res.GetStatus().GetCode()).To(Equal(expectedStatus.GetCode())) 263 Expect(res.GetStatus().GetMessage()).To(ContainSubstring(expectedStatus.GetMessage())) 264 } 265 }, 266 Entry( 267 "stat the resource errors", 268 &collaborationpb.UpdateReceivedShareRequest{ 269 UpdateMask: &fieldmaskpb.FieldMask{ 270 Paths: []string{"state"}, 271 }, 272 Share: &collaborationpb.ReceivedShare{ 273 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 274 Share: &collaborationpb.Share{ 275 Id: &collaborationpb.ShareId{ 276 OpaqueId: "1", 277 }, 278 }, 279 }, 280 }, 281 nil, 282 errors.New("some"), 283 ), 284 Entry( 285 "stat the resource fails", 286 &collaborationpb.UpdateReceivedShareRequest{ 287 UpdateMask: &fieldmaskpb.FieldMask{ 288 Paths: []string{"state"}, 289 }, 290 Share: &collaborationpb.ReceivedShare{ 291 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 292 Share: &collaborationpb.Share{ 293 Id: &collaborationpb.ShareId{ 294 OpaqueId: "1", 295 }, 296 }, 297 }, 298 }, 299 status.NewInvalid(ctx, "something"), 300 nil, 301 ), 302 ) 303 }) 304 305 Describe("CreateShare", func() { 306 DescribeTable("only requests with sufficient permissions get passed to the manager", 307 func( 308 resourceInfoPermissions *providerpb.ResourcePermissions, 309 grantPermissions *providerpb.ResourcePermissions, 310 checkPermissionStatusCode rpcpb.Code, 311 expectedCode rpcpb.Code, 312 expectedCalls int, 313 ) { 314 manager.On("Share", mock.Anything, mock.Anything, mock.Anything).Return(&collaborationpb.Share{}, nil) 315 checkPermissionResponse.Status.Code = checkPermissionStatusCode 316 317 statResourceResponse.Info.PermissionSet = resourceInfoPermissions 318 319 createShareResponse, err := provider.CreateShare(ctx, &collaborationpb.CreateShareRequest{ 320 ResourceInfo: &providerpb.ResourceInfo{ 321 PermissionSet: resourceInfoPermissions, 322 }, 323 Grant: &collaborationpb.ShareGrant{ 324 Permissions: &collaborationpb.SharePermissions{ 325 Permissions: grantPermissions, 326 }, 327 }, 328 }) 329 330 Expect(err).ToNot(HaveOccurred()) 331 Expect(createShareResponse.Status.Code).To(Equal(expectedCode)) 332 333 manager.AssertNumberOfCalls(GinkgoT(), "Share", expectedCalls) 334 }, 335 Entry( 336 "insufficient permissions", 337 conversions.RoleFromName("spaceeditor").CS3ResourcePermissions(), 338 conversions.RoleFromName("manager").CS3ResourcePermissions(), 339 rpcpb.Code_CODE_OK, 340 rpcpb.Code_CODE_INVALID_ARGUMENT, 341 0, 342 ), 343 Entry( 344 "sufficient permissions", 345 conversions.RoleFromName("manager").CS3ResourcePermissions(), 346 conversions.RoleFromName("spaceeditor").CS3ResourcePermissions(), 347 rpcpb.Code_CODE_OK, 348 rpcpb.Code_CODE_OK, 349 1, 350 ), 351 Entry( 352 "no AddGrant permission on resource", 353 cs3permissionsNoAddGrant, 354 conversions.RoleFromName("spaceeditor").CS3ResourcePermissions(), 355 rpcpb.Code_CODE_OK, 356 rpcpb.Code_CODE_PERMISSION_DENIED, 357 0, 358 ), 359 Entry( 360 "no WriteShare permission on user role", 361 conversions.RoleFromName("manager").CS3ResourcePermissions(), 362 conversions.RoleFromName("mspaceeditor").CS3ResourcePermissions(), 363 rpcpb.Code_CODE_PERMISSION_DENIED, 364 rpcpb.Code_CODE_PERMISSION_DENIED, 365 0, 366 ), 367 ) 368 Context("resharing is not allowed", func() { 369 JustBeforeEach(func() { 370 rgrpcService := usershareprovider.New(gatewaySelector, manager, []*regexp.Regexp{}) 371 372 provider = rgrpcService.(collaborationpb.CollaborationAPIServer) 373 Expect(provider).ToNot(BeNil()) 374 375 // user has list grants access 376 statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ 377 AddGrant: true, 378 ListGrants: true, 379 } 380 }) 381 DescribeTable("rejects shares with any grant changing permissions", 382 func( 383 resourceInfoPermissions *providerpb.ResourcePermissions, 384 grantPermissions *providerpb.ResourcePermissions, 385 responseCode rpcpb.Code, 386 expectedCalls int, 387 ) { 388 manager.On("Share", mock.Anything, mock.Anything, mock.Anything).Return(&collaborationpb.Share{}, nil) 389 390 createShareResponse, err := provider.CreateShare(ctx, &collaborationpb.CreateShareRequest{ 391 ResourceInfo: &providerpb.ResourceInfo{ 392 PermissionSet: resourceInfoPermissions, 393 }, 394 Grant: &collaborationpb.ShareGrant{ 395 Permissions: &collaborationpb.SharePermissions{ 396 Permissions: grantPermissions, 397 }, 398 }, 399 }) 400 401 Expect(err).ToNot(HaveOccurred()) 402 Expect(createShareResponse.Status.Code).To(Equal(responseCode)) 403 404 manager.AssertNumberOfCalls(GinkgoT(), "Share", expectedCalls) 405 }, 406 Entry("AddGrant", conversions.RoleFromName("manager").CS3ResourcePermissions(), &providerpb.ResourcePermissions{AddGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), 407 Entry("UpdateGrant", conversions.RoleFromName("manager").CS3ResourcePermissions(), &providerpb.ResourcePermissions{UpdateGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), 408 Entry("RemoveGrant", conversions.RoleFromName("manager").CS3ResourcePermissions(), &providerpb.ResourcePermissions{RemoveGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), 409 Entry("DenyGrant", conversions.RoleFromName("manager").CS3ResourcePermissions(), &providerpb.ResourcePermissions{DenyGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), 410 Entry("ListGrants", conversions.RoleFromName("manager").CS3ResourcePermissions(), &providerpb.ResourcePermissions{ListGrants: true}, rpcpb.Code_CODE_OK, 1), 411 ) 412 }) 413 }) 414 Describe("UpdateShare", func() { 415 It("fails without WriteShare permission in user role", func() { 416 checkPermissionResponse.Status.Code = rpcpb.Code_CODE_PERMISSION_DENIED 417 418 updateShareResponse, err := provider.UpdateShare(ctx, &collaborationpb.UpdateShareRequest{ 419 Ref: &collaborationpb.ShareReference{ 420 Spec: &collaborationpb.ShareReference_Id{ 421 Id: &collaborationpb.ShareId{ 422 OpaqueId: "shareid", 423 }, 424 }, 425 }, 426 Share: &collaborationpb.Share{}, 427 UpdateMask: &fieldmaskpb.FieldMask{ 428 Paths: []string{"permissions"}, 429 }, 430 }) 431 432 Expect(err).ToNot(HaveOccurred()) 433 Expect(updateShareResponse.Status.Code).To(Equal(rpcpb.Code_CODE_PERMISSION_DENIED)) 434 435 manager.AssertNumberOfCalls(GinkgoT(), "UpdateShare", 0) 436 }) 437 It("fails when the user tries to share with elevated permissions", func() { 438 // user has only read access 439 statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ 440 InitiateFileDownload: true, 441 Stat: true, 442 } 443 444 // user tries to update a share to give write permissions 445 updateShareResponse, err := provider.UpdateShare(ctx, &collaborationpb.UpdateShareRequest{ 446 Ref: &collaborationpb.ShareReference{ 447 Spec: &collaborationpb.ShareReference_Id{ 448 Id: &collaborationpb.ShareId{ 449 OpaqueId: "shareid", 450 }, 451 }, 452 }, 453 Share: &collaborationpb.Share{ 454 Permissions: &collaborationpb.SharePermissions{ 455 Permissions: &providerpb.ResourcePermissions{ 456 Stat: true, 457 InitiateFileDownload: true, 458 InitiateFileUpload: true, 459 }, 460 }, 461 }, 462 UpdateMask: &fieldmaskpb.FieldMask{ 463 Paths: []string{"permissions"}, 464 }, 465 }) 466 467 Expect(err).ToNot(HaveOccurred()) 468 Expect(updateShareResponse.Status.Code).To(Equal(rpcpb.Code_CODE_PERMISSION_DENIED)) 469 470 manager.AssertNumberOfCalls(GinkgoT(), "UpdateShare", 0) 471 }) 472 It("succeeds when the user is not the owner/creator and does not have the UpdateGrant permissions", func() { 473 // user has only read access 474 statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ 475 InitiateFileDownload: true, 476 Stat: true, 477 } 478 bobId := &userpb.UserId{OpaqueId: "bob"} 479 getShareResponse.Owner = bobId 480 getShareResponse.Creator = bobId 481 482 // user tries to update a share to give write permissions 483 updateShareResponse, err := provider.UpdateShare(ctx, &collaborationpb.UpdateShareRequest{ 484 Ref: &collaborationpb.ShareReference{ 485 Spec: &collaborationpb.ShareReference_Id{ 486 Id: &collaborationpb.ShareId{ 487 OpaqueId: "shareid", 488 }, 489 }, 490 }, 491 Share: &collaborationpb.Share{ 492 Permissions: &collaborationpb.SharePermissions{ 493 Permissions: &providerpb.ResourcePermissions{ 494 Stat: true, 495 InitiateFileDownload: true, 496 }, 497 }, 498 }, 499 UpdateMask: &fieldmaskpb.FieldMask{ 500 Paths: []string{"permissions"}, 501 }, 502 }) 503 504 Expect(err).ToNot(HaveOccurred()) 505 Expect(updateShareResponse.Status.Code).To(Equal(rpcpb.Code_CODE_PERMISSION_DENIED)) 506 507 manager.AssertNumberOfCalls(GinkgoT(), "UpdateShare", 0) 508 }) 509 It("succeeds when the user is the owner/creator", func() { 510 // user has only read access 511 statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ 512 InitiateFileDownload: true, 513 Stat: true, 514 } 515 516 // user tries to update a share to give write permissions 517 updateShareResponse, err := provider.UpdateShare(ctx, &collaborationpb.UpdateShareRequest{ 518 Ref: &collaborationpb.ShareReference{ 519 Spec: &collaborationpb.ShareReference_Id{ 520 Id: &collaborationpb.ShareId{ 521 OpaqueId: "shareid", 522 }, 523 }, 524 }, 525 Share: &collaborationpb.Share{ 526 Permissions: &collaborationpb.SharePermissions{ 527 Permissions: &providerpb.ResourcePermissions{ 528 Stat: true, 529 InitiateFileDownload: true, 530 }, 531 }, 532 }, 533 UpdateMask: &fieldmaskpb.FieldMask{ 534 Paths: []string{"permissions"}, 535 }, 536 }) 537 538 Expect(err).ToNot(HaveOccurred()) 539 Expect(updateShareResponse.Status.Code).To(Equal(rpcpb.Code_CODE_OK)) 540 541 manager.AssertNumberOfCalls(GinkgoT(), "UpdateShare", 1) 542 }) 543 It("succeeds when the user is not the owner/creator but has the UpdateGrant permissions", func() { 544 // user has only read access 545 statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ 546 UpdateGrant: true, 547 InitiateFileDownload: true, 548 Stat: true, 549 } 550 bobId := &userpb.UserId{OpaqueId: "bob"} 551 getShareResponse.Owner = bobId 552 getShareResponse.Creator = bobId 553 554 // user tries to update a share to give write permissions 555 updateShareResponse, err := provider.UpdateShare(ctx, &collaborationpb.UpdateShareRequest{ 556 Ref: &collaborationpb.ShareReference{ 557 Spec: &collaborationpb.ShareReference_Id{ 558 Id: &collaborationpb.ShareId{ 559 OpaqueId: "shareid", 560 }, 561 }, 562 }, 563 Share: &collaborationpb.Share{ 564 Permissions: &collaborationpb.SharePermissions{ 565 Permissions: &providerpb.ResourcePermissions{ 566 Stat: true, 567 InitiateFileDownload: true, 568 }, 569 }, 570 }, 571 UpdateMask: &fieldmaskpb.FieldMask{ 572 Paths: []string{"permissions"}, 573 }, 574 }) 575 576 Expect(err).ToNot(HaveOccurred()) 577 Expect(updateShareResponse.Status.Code).To(Equal(rpcpb.Code_CODE_OK)) 578 579 manager.AssertNumberOfCalls(GinkgoT(), "UpdateShare", 1) 580 }) 581 }) 582 }) 583 584 var _ = Describe("helpers", func() { 585 type GetMountpointAndUnmountedSharesArgs struct { 586 withName string 587 withResourceId *providerpb.ResourceId 588 listReceivedSharesResponse *collaborationpb.ListReceivedSharesResponse 589 listReceivedSharesError error 590 expectedName string 591 } 592 DescribeTable("GetMountpointAndUnmountedShares", 593 func(args GetMountpointAndUnmountedSharesArgs) { 594 gatewayClient := cs3mocks.NewGatewayAPIClient(GinkgoT()) 595 596 gatewayClient.EXPECT(). 597 ListReceivedShares(mock.Anything, mock.Anything). 598 RunAndReturn(func(ctx context.Context, request *collaborationpb.ListReceivedSharesRequest, option ...grpc.CallOption) (*collaborationpb.ListReceivedSharesResponse, error) { 599 return args.listReceivedSharesResponse, args.listReceivedSharesError 600 }) 601 602 statCallCount := 0 603 604 for _, s := range args.listReceivedSharesResponse.GetShares() { 605 if s.GetState() != collaborationpb.ShareState_SHARE_STATE_ACCEPTED { 606 continue 607 } 608 609 // add one for every accepted share where the resource id matches 610 if utils.ResourceIDEqual(s.GetShare().GetResourceId(), args.withResourceId) { 611 statCallCount++ 612 } 613 614 // add one for every accepted share where the mountpoint patch matches 615 if s.GetMountPoint().GetPath() == filepath.Clean(args.withName) { 616 statCallCount++ 617 } 618 } 619 620 if statCallCount > 0 { 621 gatewayClient.EXPECT(). 622 Stat(mock.Anything, mock.Anything, mock.Anything). 623 RunAndReturn(func(ctx context.Context, request *providerpb.StatRequest, option ...grpc.CallOption) (*providerpb.StatResponse, error) { 624 return &providerpb.StatResponse{ 625 Status: status.NewOK(ctx), 626 }, nil 627 }).Times(statCallCount) 628 } 629 630 availableMountpoint, _, err := usershareprovider.GetMountpointAndUnmountedShares(context.Background(), gatewayClient, args.withResourceId, args.withName, nil) 631 632 if args.listReceivedSharesError != nil { 633 Expect(err).To(HaveOccurred(), "expected error, got none") 634 return 635 } 636 637 Expect(availableMountpoint).To(Equal(args.expectedName), "expected mountpoint %s, got %s", args.expectedName, availableMountpoint) 638 639 gatewayClient.EXPECT().Stat(mock.Anything, mock.Anything, mock.Anything).Unset() 640 }, 641 Entry( 642 "listing received shares errors", 643 GetMountpointAndUnmountedSharesArgs{ 644 listReceivedSharesError: errors.New("some error"), 645 }, 646 ), 647 Entry( 648 "returns the given name if no shares are found", 649 GetMountpointAndUnmountedSharesArgs{ 650 withName: "name1", 651 listReceivedSharesResponse: &collaborationpb.ListReceivedSharesResponse{ 652 Status: status.NewOK(context.Background()), 653 }, 654 expectedName: "name1", 655 }, 656 ), 657 Entry( 658 "returns the path as name if a share with the same resourceId is found", 659 GetMountpointAndUnmountedSharesArgs{ 660 withName: "name", 661 withResourceId: &providerpb.ResourceId{ 662 StorageId: "1", 663 OpaqueId: "2", 664 SpaceId: "3", 665 }, 666 listReceivedSharesResponse: &collaborationpb.ListReceivedSharesResponse{ 667 Status: status.NewOK(context.Background()), 668 Shares: []*collaborationpb.ReceivedShare{ 669 { 670 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 671 MountPoint: &providerpb.Reference{ 672 Path: "path", 673 }, 674 Share: &collaborationpb.Share{ 675 ResourceId: &providerpb.ResourceId{ 676 StorageId: "1", 677 OpaqueId: "2", 678 SpaceId: "3", 679 }, 680 }, 681 }, 682 }, 683 }, 684 expectedName: "path", 685 }, 686 ), 687 Entry( 688 "enumerates the name if a share with the same path already exists", 689 GetMountpointAndUnmountedSharesArgs{ 690 withName: "some name", 691 listReceivedSharesResponse: &collaborationpb.ListReceivedSharesResponse{ 692 Status: status.NewOK(context.Background()), 693 Shares: []*collaborationpb.ReceivedShare{ 694 { 695 State: collaborationpb.ShareState_SHARE_STATE_ACCEPTED, 696 MountPoint: &providerpb.Reference{ 697 Path: "some name", 698 }, 699 Share: &collaborationpb.Share{ 700 ResourceId: &providerpb.ResourceId{ 701 StorageId: "1", 702 OpaqueId: "2", 703 SpaceId: "3", 704 }, 705 }, 706 }, 707 }, 708 }, 709 expectedName: "some name (1)", 710 }, 711 ), 712 ) 713 })