From 7ba2c3f1f0ac3c42e81e82cfb8bb0a7e8d700a32 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Tue, 7 May 2024 22:14:42 +0200 Subject: [PATCH] tests: unit tests for update public shares --- .../publicshareprovider_test.go | 506 +++++++++++++++++- 1 file changed, 488 insertions(+), 18 deletions(-) diff --git a/internal/grpc/services/publicshareprovider/publicshareprovider_test.go b/internal/grpc/services/publicshareprovider/publicshareprovider_test.go index d4ced5509a8..07489ce697e 100644 --- a/internal/grpc/services/publicshareprovider/publicshareprovider_test.go +++ b/internal/grpc/services/publicshareprovider/publicshareprovider_test.go @@ -12,6 +12,7 @@ import ( providerpb "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/internal/grpc/services/publicshareprovider" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/permission" "github.com/cs3org/reva/v2/pkg/publicshare" "github.com/cs3org/reva/v2/pkg/publicshare/manager/registry" "github.com/cs3org/reva/v2/pkg/publicshare/mocks" @@ -130,14 +131,6 @@ var _ = Describe("PublicShareProvider", func() { InitiateFileUpload: true, } - createdLink = &link.PublicShare{ - PasswordProtected: true, - Permissions: &link.PublicSharePermissions{ - Permissions: linkPermissions, - }, - Creator: user.Id, - } - revaConfig = map[string]interface{}{ "driver": "mockManager", "drivers": map[string]map[string]interface{}{ @@ -182,6 +175,14 @@ var _ = Describe("PublicShareProvider", func() { EXPECT(). Stat(mock.Anything, mock.Anything). Return(statResourceResponse, nil) + + createdLink = &link.PublicShare{ + PasswordProtected: true, + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + Creator: user.Id, + } }) It("creates a public share with password", func() { manager. @@ -260,9 +261,13 @@ var _ = Describe("PublicShareProvider", func() { Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_OK)) Expect(res.GetShare()).To(Equal(createdLink)) }) - It("has no share permission on the resource to create internal link", func() { + It("has no share permission on the resource, can create internal link", func() { // internal links are created with empty permissions linkPermissions := &providerpb.ResourcePermissions{} + manager. + EXPECT(). + CreatePublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(createdLink, nil) // internal link creation should not check user permissions gatewayClient.EXPECT().CheckPermission(mock.Anything, mock.Anything).Unset() @@ -287,8 +292,8 @@ var _ = Describe("PublicShareProvider", func() { res, err := provider.CreatePublicShare(ctx, req) Expect(err).ToNot(HaveOccurred()) - Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT)) - Expect(res.GetStatus().GetMessage()).To(Equal("no share permission")) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_OK)) + Expect(res.GetShare()).To(Equal(createdLink)) }) It("fails to check create public share user permission", func() { gatewayClient.EXPECT().CheckPermission(mock.Anything, mock.Anything).Unset() @@ -427,12 +432,6 @@ var _ = Describe("PublicShareProvider", func() { }, ArbitraryMetadata: &providerpb.ArbitraryMetadata{Metadata: map[string]string{"quicklink": "true"}}, Path: "./NewFolder/file.txt", - Id: &providerpb.ResourceId{ - StorageId: "storage-id", - OpaqueId: "project-id", - SpaceId: "project-id", - }, - Space: &providerpb.StorageSpace{SpaceType: "project"}, }, Grant: &link.Grant{ Permissions: &link.PublicSharePermissions{ @@ -531,7 +530,7 @@ var _ = Describe("PublicShareProvider", func() { Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT)) Expect(res.GetStatus().GetMessage()).To(ContainSubstring("expiration date is in the past")) }) - It("create public share with valid expiration", func() { + It("create public share with valid expiration date", func() { tomorrow := time.Now().AddDate(0, 0, 1) tsTomorrow := utils.TimeToTS(tomorrow) @@ -921,4 +920,475 @@ var _ = Describe("PublicShareProvider", func() { }) }) }) + Describe("Updating a PublicShare", func() { + var ( + existingLink *link.PublicShare + updatedLink *link.PublicShare + ) + + BeforeEach(func() { + // stat the shared resource to get the users resource permissions + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Return(statResourceResponse, nil) + + existingLink = &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + Permissions: &link.PublicSharePermissions{ + Permissions: &providerpb.ResourcePermissions{ + Stat: true, + ListContainer: true, + InitiateFileDownload: true, + AddGrant: true, + }, + }, + Creator: user.GetId(), + } + }) + Context("when the user downgrades a public link to internal", func() { + BeforeEach(func() { + linkPermissions = &providerpb.ResourcePermissions{} + updatedLink = &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + DisplayName: "Updated Link", + } + }) + It("fails when it cannot load the existing share", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("transport error")) + + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).To(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INTERNAL)) + }) + It("fails when it cannot connect to the storage provider", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Return(nil, errors.New("transport error")) + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).To(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INTERNAL)) + }) + It("fails when it cannot find the shared resource", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + + statResourceResponse.Status = status.NewNotFound(context.TODO(), "not found") + statResourceResponse.Info = nil + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Return(statResourceResponse, nil) + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_NOT_FOUND)) + }) + It("fails when it cannot store share information", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + manager. + EXPECT(). + UpdatePublicShare(mock.Anything, mock.Anything, mock.Anything). + Once().Return(nil, errors.New("storage error")) + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INTERNAL)) + Expect(res.GetStatus().GetMessage()).To(Equal("storage error")) + }) + It("fails when the user is neither the creator nor the owner of the share", func() { + existingLink.Creator = &userpb.UserId{ + OpaqueId: "admin", + } + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + checkPermissionResponse.Status.Code = rpc.Code_CODE_PERMISSION_DENIED + gatewayClient. + EXPECT(). + CheckPermission(mock.Anything, mock.Anything). + Return(checkPermissionResponse, nil) + + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + + Ref: &link.PublicShareReference{ + Spec: &link.PublicShareReference_Id{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_PERMISSION_DENIED)) + + }) + It("fails when user is neither the resource owner nor the share creator", func() { + existingLink.Creator = &userpb.UserId{ + OpaqueId: "admin", + } + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + checkPermissionResponse.Status.Code = rpc.Code_CODE_PERMISSION_DENIED + gatewayClient. + EXPECT(). + CheckPermission(mock.Anything, mock.Anything). + Return(checkPermissionResponse, nil) + + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + + linkPermissions.Delete = true + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + + Ref: &link.PublicShareReference{ + Spec: &link.PublicShareReference_Id{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_PERMISSION_DENIED)) + + }) + It("succeeds", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + manager. + EXPECT(). + UpdatePublicShare(mock.Anything, mock.Anything, mock.Anything). + Once().Return(updatedLink, nil) + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + + Ref: &link.PublicShareReference{ + Spec: &link.PublicShareReference_Id{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_OK)) + Expect(res.GetShare()).To(Equal(updatedLink)) + }) + It("succeeds even if the user is no manager or owner on the resource", func() { + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + + manager. + EXPECT(). + UpdatePublicShare(mock.Anything, mock.Anything, mock.Anything). + Once().Return(updatedLink, nil) + + statResourceResponse.Info.PermissionSet.UpdateGrant = false + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + + Ref: &link.PublicShareReference{ + Spec: &link.PublicShareReference_Id{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_OK)) + Expect(res.GetShare()).To(Equal(updatedLink)) + }) + }) + Context("when the user changes permissions", func() { + BeforeEach(func() { + linkPermissions = &providerpb.ResourcePermissions{ + Stat: true, + AddGrant: true, + Delete: true, + Move: true, + InitiateFileDownload: true, + InitiateFileUpload: true, + CreateContainer: true, + ListContainer: true, + ListGrants: true, + } + + updatedLink = &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: "share-id", + }, + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + DisplayName: "Updated Link", + } + + manager. + EXPECT(). + GetPublicShare(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Once().Return(existingLink, nil) + }) + It("fails when the user has not enough permissions on the resource", func() { + statResourceResponse.Info.PermissionSet.Delete = false + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT)) + Expect(res.GetStatus().GetMessage()).To(Equal("insufficient permissions to update that kind of share")) + }) + It("fails when the user has no permission to write public shares and is not the creator", func() { + checkPermissionResponse.Status.Code = rpc.Code_CODE_PERMISSION_DENIED + gatewayClient. + EXPECT(). + CheckPermission(mock.Anything, mock.Anything). + Return(checkPermissionResponse, nil) + + existingLink.Creator = &userpb.UserId{ + OpaqueId: "admin", + } + gatewayClient. + EXPECT(). + Stat(mock.Anything, mock.Anything). + Unset() + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_PERMISSION_DENIED)) + Expect(res.GetStatus().GetMessage()).To(Equal("no permission to update public share")) + }) + It("fails when the user is not the creator and has no manager or owner permissions", func() { + existingLink.Creator = &userpb.UserId{ + OpaqueId: "admin", + } + + gatewayClient. + EXPECT(). + CheckPermission(mock.Anything, mock.Anything). + Return(checkPermissionResponse, nil) + + statResourceResponse.Info.PermissionSet.UpdateGrant = false + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS, + Grant: &link.Grant{ + Permissions: &link.PublicSharePermissions{ + Permissions: linkPermissions, + }, + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_PERMISSION_DENIED)) + Expect(res.GetStatus().GetMessage()).To(Equal("no permission to update public share")) + }) + It("fails when the expiration date is in the past", func() { + yesterday := time.Now().AddDate(0, 0, -1) + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION, + Grant: &link.Grant{ + Expiration: utils.TimeToTS(yesterday), + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT)) + Expect(res.GetStatus().GetMessage()).To(ContainSubstring("expiration date is in the past")) + }) + It("fails when the password is empty on a writable share", func() { + gatewayClient. + EXPECT(). + CheckPermission( + mock.Anything, + mock.MatchedBy( + func(req *permissions.CheckPermissionRequest) bool { + return req.GetPermission() == permission.DeleteReadOnlyPassword + }, + ), + ). + Return(checkPermissionResponse, nil) + + req := &link.UpdatePublicShareRequest{ + Update: &link.UpdatePublicShareRequest_Update{ + Type: link.UpdatePublicShareRequest_Update_TYPE_PASSWORD, + Grant: &link.Grant{ + Password: "", + }, + }, + } + + res, err := provider.UpdatePublicShare(ctx, req) + Expect(err).ToNot(HaveOccurred()) + Expect(res.GetStatus().GetCode()).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT)) + Expect(res.GetStatus().GetMessage()).To(ContainSubstring("password protection is enforced")) + }) + }) + }) })