diff --git a/changelog/unreleased/allow-disabling-resharing.md b/changelog/unreleased/allow-disabling-resharing.md new file mode 100644 index 0000000000..73e6347dee --- /dev/null +++ b/changelog/unreleased/allow-disabling-resharing.md @@ -0,0 +1,5 @@ +Bugfix: the sharemanager can now reject grants with resharing permissions + +When disabling resharing we also need to prevent grants from allowing any grant permissions. + +https://github.com/cs3org/reva/pull/4516 diff --git a/internal/grpc/services/usershareprovider/usershareprovider.go b/internal/grpc/services/usershareprovider/usershareprovider.go index 0710d00786..0c503e7cc2 100644 --- a/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/internal/grpc/services/usershareprovider/usershareprovider.go @@ -55,6 +55,7 @@ type config struct { Drivers map[string]map[string]interface{} `mapstructure:"drivers"` GatewayAddr string `mapstructure:"gateway_addr"` AllowedPathsForShares []string `mapstructure:"allowed_paths_for_shares"` + DisableResharing bool `mapstructure:"disable_resharing"` } func (c *config) init() { @@ -67,6 +68,7 @@ type service struct { sm share.Manager gatewaySelector pool.Selectable[gateway.GatewayAPIClient] allowedPathsForShares []*regexp.Regexp + disableResharing bool } func getShareManager(c *config) (share.Manager, error) { @@ -127,15 +129,16 @@ func NewDefault(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error return nil, err } - return New(gatewaySelector, sm, allowedPathsForShares), nil + return New(gatewaySelector, sm, allowedPathsForShares, c.DisableResharing), nil } // New creates a new user share provider svc -func New(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], sm share.Manager, allowedPathsForShares []*regexp.Regexp) rgrpc.Service { +func New(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], sm share.Manager, allowedPathsForShares []*regexp.Regexp, disableResharing bool) rgrpc.Service { service := &service{ sm: sm, gatewaySelector: gatewaySelector, allowedPathsForShares: allowedPathsForShares, + disableResharing: disableResharing, } return service @@ -157,6 +160,13 @@ func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShar log := appctx.GetLogger(ctx) user := ctxpkg.ContextMustGetUser(ctx) + // when resharing is disabled grants must not allow grant permissions + if s.disableResharing && HasGrantPermissions(req.GetGrant().GetPermissions().GetPermissions()) { + return &collaboration.CreateShareResponse{ + Status: status.NewInvalidArg(ctx, "resharing not supported"), + }, nil + } + gatewayClient, err := s.gatewaySelector.Next() if err != nil { return nil, err @@ -235,6 +245,10 @@ func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShar }, nil } +func HasGrantPermissions(p *provider.ResourcePermissions) bool { + return p.GetAddGrant() || p.GetUpdateGrant() || p.GetRemoveGrant() || p.GetDenyGrant() +} + func (s *service) RemoveShare(ctx context.Context, req *collaboration.RemoveShareRequest) (*collaboration.RemoveShareResponse, error) { log := appctx.GetLogger(ctx) user := ctxpkg.ContextMustGetUser(ctx) @@ -327,6 +341,14 @@ func (s *service) ListShares(ctx context.Context, req *collaboration.ListSharesR func (s *service) UpdateShare(ctx context.Context, req *collaboration.UpdateShareRequest) (*collaboration.UpdateShareResponse, error) { log := appctx.GetLogger(ctx) user := ctxpkg.ContextMustGetUser(ctx) + + // when resharing is disabled grants must not allow grant permissions + if s.disableResharing && HasGrantPermissions(req.GetShare().GetPermissions().GetPermissions()) { + return &collaboration.UpdateShareResponse{ + Status: status.NewInvalidArg(ctx, "resharing not supported"), + }, nil + } + gatewayClient, err := s.gatewaySelector.Next() if err != nil { return nil, err diff --git a/internal/grpc/services/usershareprovider/usershareprovider_test.go b/internal/grpc/services/usershareprovider/usershareprovider_test.go index 7e9b4347a1..7b958eda4c 100644 --- a/internal/grpc/services/usershareprovider/usershareprovider_test.go +++ b/internal/grpc/services/usershareprovider/usershareprovider_test.go @@ -112,7 +112,7 @@ var _ = Describe("user share provider service", func() { } manager.On("GetShare", mock.Anything, mock.Anything).Return(getShareResponse, nil) - rgrpcService := usershareprovider.New(gatewaySelector, manager, []*regexp.Regexp{}) + rgrpcService := usershareprovider.New(gatewaySelector, manager, []*regexp.Regexp{}, false) provider = rgrpcService.(collaborationpb.CollaborationAPIServer) Expect(provider).ToNot(BeNil()) @@ -183,6 +183,52 @@ var _ = Describe("user share provider service", func() { 0, ), ) + Context("resharing disabled", func() { + JustBeforeEach(func() { + // disable resharing + rgrpcService := usershareprovider.New(gatewaySelector, manager, []*regexp.Regexp{}, true) + + provider = rgrpcService.(collaborationpb.CollaborationAPIServer) + Expect(provider).ToNot(BeNil()) + + // user has list grants access + statResourceResponse.Info.PermissionSet = &providerpb.ResourcePermissions{ + AddGrant: true, + ListGrants: true, + } + }) + DescribeTable("rejects shares with any grant changing permissions", + func( + resourceInfoPermissions *providerpb.ResourcePermissions, + grantPermissions *providerpb.ResourcePermissions, + responseCode rpcpb.Code, + expectedCalls int, + ) { + manager.On("Share", mock.Anything, mock.Anything, mock.Anything).Return(&collaborationpb.Share{}, nil) + + createShareResponse, err := provider.CreateShare(ctx, &collaborationpb.CreateShareRequest{ + ResourceInfo: &providerpb.ResourceInfo{ + PermissionSet: resourceInfoPermissions, + }, + Grant: &collaborationpb.ShareGrant{ + Permissions: &collaborationpb.SharePermissions{ + Permissions: grantPermissions, + }, + }, + }) + + Expect(err).ToNot(HaveOccurred()) + Expect(createShareResponse.Status.Code).To(Equal(responseCode)) + + manager.AssertNumberOfCalls(GinkgoT(), "Share", expectedCalls) + }, + Entry("AddGrant", conversions.RoleFromName("manager", true).CS3ResourcePermissions(), &providerpb.ResourcePermissions{AddGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), + Entry("UpdateGrant", conversions.RoleFromName("manager", true).CS3ResourcePermissions(), &providerpb.ResourcePermissions{UpdateGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), + Entry("RemoveGrant", conversions.RoleFromName("manager", true).CS3ResourcePermissions(), &providerpb.ResourcePermissions{RemoveGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), + Entry("DenyGrant", conversions.RoleFromName("manager", true).CS3ResourcePermissions(), &providerpb.ResourcePermissions{DenyGrant: true}, rpcpb.Code_CODE_INVALID_ARGUMENT, 0), + Entry("ListGrants", conversions.RoleFromName("manager", true).CS3ResourcePermissions(), &providerpb.ResourcePermissions{ListGrants: true}, rpcpb.Code_CODE_OK, 1), + ) + }) }) Describe("UpdateShare", func() { It("fails without WriteShare permission in user role", func() {