diff --git a/changelog/unreleased/allow-listreceivedshares-serviceaccounts.md b/changelog/unreleased/allow-listreceivedshares-serviceaccounts.md new file mode 100644 index 00000000000..c7b97a7e2d6 --- /dev/null +++ b/changelog/unreleased/allow-listreceivedshares-serviceaccounts.md @@ -0,0 +1,5 @@ +Enhancement: Allow listing reveived shares by service accounts + +Similar to UpdateReceivedShare we now pass a forUser parameter to list received shares when using service accounts + +https://github.com/cs3org/reva/pull/4244 diff --git a/internal/grpc/services/usershareprovider/usershareprovider.go b/internal/grpc/services/usershareprovider/usershareprovider.go index b850b5f4df7..2486031118d 100644 --- a/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/internal/grpc/services/usershareprovider/usershareprovider.go @@ -246,7 +246,10 @@ func (s *service) ListReceivedShares(ctx context.Context, req *collaboration.Lis if !foundExclude { req.Filters = append(req.Filters, &collaboration.Filter{Type: collaboration.Filter_TYPE_EXCLUDE_DENIALS}) } - shares, err := s.sm.ListReceivedShares(ctx, req.Filters) // TODO(labkode): check what to update + + var uid userpb.UserId + _ = utils.ReadJSONFromOpaque(req.Opaque, "userid", &uid) + shares, err := s.sm.ListReceivedShares(ctx, req.Filters, &uid) // TODO(labkode): check what to update if err != nil { return &collaboration.ListReceivedSharesResponse{ Status: status.NewInternal(ctx, "error listing received shares"), diff --git a/pkg/cbox/share/sql/sql.go b/pkg/cbox/share/sql/sql.go index 813a967b119..b9bd55367c3 100644 --- a/pkg/cbox/share/sql/sql.go +++ b/pkg/cbox/share/sql/sql.go @@ -323,7 +323,7 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ( } // we list the shares that are targeted to the user in context or to the user groups. -func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, _ *userpb.UserId) ([]*collaboration.ReceivedShare, error) { user := ctxpkg.ContextMustGetUser(ctx) uid := conversions.FormatUserID(user.Id) diff --git a/pkg/share/manager/cs3/cs3.go b/pkg/share/manager/cs3/cs3.go index f0ac9a13510..35095ca0636 100644 --- a/pkg/share/manager/cs3/cs3.go +++ b/pkg/share/manager/cs3/cs3.go @@ -519,7 +519,7 @@ func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareRefer } // ListReceivedShares returns the list of shares the user has access to. -func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) { if err := m.initialize(); err != nil { return nil, err } @@ -529,11 +529,20 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati return nil, errtypes.UserRequired("error getting user from context") } + uid, groups := user.GetId(), user.GetGroups() + if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { + u, err := utils.GetUser(forUser, m.gatewayClient) + if err != nil { + return nil, errtypes.BadRequest("user not found") + } + uid = forUser + groups = u.GetGroups() + } result := []*collaboration.ReceivedShare{} ids, err := granteeToIndex(&provider.Grantee{ Type: provider.GranteeType_GRANTEE_TYPE_USER, - Id: &provider.Grantee_UserId{UserId: user.Id}, + Id: &provider.Grantee_UserId{UserId: uid}, }) if err != nil { return nil, err @@ -544,7 +553,7 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati if err != nil { return nil, err } - for _, group := range user.Groups { + for _, group := range groups { index, err := granteeToIndex(&provider.Grantee{ Type: provider.GranteeType_GRANTEE_TYPE_GROUP, Id: &provider.Grantee_GroupId{GroupId: &groupv1beta1.GroupId{OpaqueId: group}}, diff --git a/pkg/share/manager/cs3/cs3_test.go b/pkg/share/manager/cs3/cs3_test.go index 9389fdeecd8..098890ee8f6 100644 --- a/pkg/share/manager/cs3/cs3_test.go +++ b/pkg/share/manager/cs3/cs3_test.go @@ -463,7 +463,7 @@ var _ = Describe("Manager", func() { }) It("list the user shares", func() { - rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(rshares).ToNot(BeNil()) Expect(len(rshares)).To(Equal(1)) @@ -493,7 +493,7 @@ var _ = Describe("Manager", func() { }) It("list the group share", func() { - rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(rshares).ToNot(BeNil()) Expect(len(rshares)).To(Equal(1)) @@ -531,7 +531,7 @@ var _ = Describe("Manager", func() { }) It("list the user and shares", func() { - rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + rshares, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(rshares).ToNot(BeNil()) Expect(len(rshares)).To(Equal(2)) @@ -545,7 +545,7 @@ var _ = Describe("Manager", func() { GranteeType: provider.GranteeType_GRANTEE_TYPE_USER, }, }, - }) + }, nil) Expect(err).ToNot(HaveOccurred()) Expect(rshares).ToNot(BeNil()) Expect(len(rshares)).To(Equal(1)) @@ -560,7 +560,7 @@ var _ = Describe("Manager", func() { GranteeType: provider.GranteeType_GRANTEE_TYPE_GROUP, }, }, - }) + }, nil) Expect(err).ToNot(HaveOccurred()) Expect(rshares).ToNot(BeNil()) Expect(len(rshares)).To(Equal(1)) diff --git a/pkg/share/manager/json/json.go b/pkg/share/manager/json/json.go index 31c20c27d5b..68f39f13780 100644 --- a/pkg/share/manager/json/json.go +++ b/pkg/share/manager/json/json.go @@ -475,11 +475,22 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ( } // we list the shares that are targeted to the user in context or to the user groups. -func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) { m.Lock() defer m.Unlock() user := ctxpkg.ContextMustGetUser(ctx) + if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + gwc, err := pool.GetGatewayServiceClient(m.c.GatewayAddr) + if err != nil { + return nil, errors.Wrap(err, "failed to list shares") + } + u, err := utils.GetUser(forUser, gwc) + if err != nil { + return nil, errtypes.BadRequest("user not found") + } + user = u + } mem := make(map[string]int) var rss []*collaboration.ReceivedShare for _, s := range m.model.Shares { diff --git a/pkg/share/manager/jsoncs3/jsoncs3.go b/pkg/share/manager/jsoncs3/jsoncs3.go index 5deba084d59..0349a410c78 100644 --- a/pkg/share/manager/jsoncs3/jsoncs3.go +++ b/pkg/share/manager/jsoncs3/jsoncs3.go @@ -759,7 +759,7 @@ func (m *Manager) listCreatedShares(ctx context.Context, user *userv1beta1.User, } // ListReceivedShares returns the list of shares the user has access to. -func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) { ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "ListReceivedShares") defer span.End() @@ -768,6 +768,13 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati } user := ctxpkg.ContextMustGetUser(ctx) + if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + u, err := utils.GetUser(forUser, m.gateway) + if err != nil { + return nil, errtypes.BadRequest("user not found") + } + user = u + } ssids := map[string]*receivedsharecache.Space{} diff --git a/pkg/share/manager/jsoncs3/jsoncs3_test.go b/pkg/share/manager/jsoncs3/jsoncs3_test.go index 3f17b9674eb..419a6222413 100644 --- a/pkg/share/manager/jsoncs3/jsoncs3_test.go +++ b/pkg/share/manager/jsoncs3/jsoncs3_test.go @@ -715,7 +715,7 @@ var _ = Describe("Jsoncs3", func() { Describe("ListReceivedShares", func() { It("lists the received shares", func() { - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) Expect(received[0].Share.ResourceId).To(Equal(sharedResource.Id)) @@ -723,7 +723,7 @@ var _ = Describe("Jsoncs3", func() { }) It("syncronizes the provider cache before listing", func() { - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) Expect(received[0].Share.Permissions.Permissions.InitiateFileUpload).To(BeFalse()) @@ -743,7 +743,7 @@ var _ = Describe("Jsoncs3", func() { cache.Shares[share.Id.OpaqueId].Permissions.Permissions.InitiateFileUpload = false cache.Etag = "reset1" // trigger reload - received, err = m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err = m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) Expect(received[0].Share.Permissions.Permissions.InitiateFileUpload).To(BeTrue()) @@ -753,7 +753,7 @@ var _ = Describe("Jsoncs3", func() { m, err := jsoncs3.New(storage, nil, 0, nil, 0) // Reset in-memory cache Expect(err).ToNot(HaveOccurred()) - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) }) @@ -762,7 +762,7 @@ var _ = Describe("Jsoncs3", func() { share2, err := m.Share(ctx, sharedResource2, grant) Expect(err).ToNot(HaveOccurred()) - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(2)) @@ -773,7 +773,7 @@ var _ = Describe("Jsoncs3", func() { ResourceId: sharedResource.Id, }, }, - }) + }, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) Expect(received[0].Share.ResourceId).To(Equal(sharedResource.Id)) @@ -787,7 +787,7 @@ var _ = Describe("Jsoncs3", func() { ResourceId: sharedResource2.Id, }, }, - }) + }, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(1)) Expect(received[0].Share.ResourceId).To(Equal(sharedResource2.Id)) @@ -807,7 +807,7 @@ var _ = Describe("Jsoncs3", func() { }) It("lists the group share", func() { - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(2)) ids := []string{} @@ -821,7 +821,7 @@ var _ = Describe("Jsoncs3", func() { m, err := jsoncs3.New(storage, nil, 0, nil, 0) // Reset in-memory cache Expect(err).ToNot(HaveOccurred()) - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(2)) ids := []string{} @@ -843,7 +843,7 @@ var _ = Describe("Jsoncs3", func() { _, err = m.UpdateReceivedShare(granteeCtx, rs, &fieldmaskpb.FieldMask{Paths: []string{"state"}}, nil) Expect(err).ToNot(HaveOccurred()) - received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}) + received, err := m.ListReceivedShares(granteeCtx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(received)).To(Equal(2)) }) diff --git a/pkg/share/manager/memory/memory.go b/pkg/share/manager/memory/memory.go index 6d2c3a17911..9c84679b472 100644 --- a/pkg/share/manager/memory/memory.go +++ b/pkg/share/manager/memory/memory.go @@ -20,6 +20,7 @@ package memory import ( "context" + "errors" "fmt" "sync" "sync/atomic" @@ -282,11 +283,20 @@ func (m *manager) ListShares(ctx context.Context, filters []*collaboration.Filte } // we list the shares that are targeted to the user in context or to the user groups. -func (m *manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) { var rss []*collaboration.ReceivedShare m.lock.Lock() defer m.lock.Unlock() user := ctxpkg.ContextMustGetUser(ctx) + if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + // TODO: gateway missing! + // u, err := utils.GetUser(forUser, nil) + // if err != nil { + // return nil, errtypes.BadRequest("user not found") + // } + // user = u + return nil, errors.New("can't use inmem share manager and service accounts") + } for _, s := range m.shares { if share.IsCreatedByUser(s, user) || !share.IsGrantedToUser(s, user) { // omit shares created by the user or shares the user can't access diff --git a/pkg/share/manager/owncloudsql/conversions.go b/pkg/share/manager/owncloudsql/conversions.go index dabab408e31..9b496ce0473 100644 --- a/pkg/share/manager/owncloudsql/conversions.go +++ b/pkg/share/manager/owncloudsql/conversions.go @@ -33,6 +33,7 @@ import ( conversions "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/v2/pkg/rgrpc/status" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/v2/pkg/utils" "github.com/jellydator/ttlcache/v2" ) @@ -62,6 +63,7 @@ type DBShare struct { type UserConverter interface { UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) + GetUser(userid *userpb.UserId) (*userpb.User, error) } // GatewayUserConverter converts usernames and ids using the gateway @@ -139,6 +141,15 @@ func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username st return getUserResponse.User.Id, nil } +// GetUser gets the user +func (c *GatewayUserConverter) GetUser(userid *userpb.UserId) (*userpb.User, error) { + gwc, err := pool.GetGatewayServiceClient(c.gwAddr) + if err != nil { + return nil, err + } + return utils.GetUser(userid, gwc) +} + func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) { var granteeType int var formattedID string diff --git a/pkg/share/manager/owncloudsql/owncloudsql.go b/pkg/share/manager/owncloudsql/owncloudsql.go index 34fe52a5a9b..c162967ed38 100644 --- a/pkg/share/manager/owncloudsql/owncloudsql.go +++ b/pkg/share/manager/owncloudsql/owncloudsql.go @@ -336,8 +336,15 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ( } // we list the shares that are targeted to the user in context or to the user groups. -func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { +func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) { user := ctxpkg.ContextMustGetUser(ctx) + if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { + u, err := m.userConverter.GetUser(forUser) + if err != nil { + return nil, errtypes.BadRequest("user not found") + } + user = u + } uid := user.Username params := []interface{}{uid, uid, uid} diff --git a/pkg/share/manager/owncloudsql/owncloudsql_test.go b/pkg/share/manager/owncloudsql/owncloudsql_test.go index 5c6d7670824..90e7b953cae 100644 --- a/pkg/share/manager/owncloudsql/owncloudsql_test.go +++ b/pkg/share/manager/owncloudsql/owncloudsql_test.go @@ -246,7 +246,7 @@ var _ = Describe("SQL manager", func() { ) Expect(err).ToNot(HaveOccurred()) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(2)) groupShare := shares[1] @@ -273,7 +273,7 @@ var _ = Describe("SQL manager", func() { ) Expect(err).ToNot(HaveOccurred()) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(2)) groupShare := shares[1] @@ -307,7 +307,7 @@ var _ = Describe("SQL manager", func() { ) Expect(err).ToNot(HaveOccurred()) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(2)) groupShare := shares[1] @@ -332,7 +332,7 @@ var _ = Describe("SQL manager", func() { ) Expect(err).ToNot(HaveOccurred()) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(1)) }) @@ -340,14 +340,14 @@ var _ = Describe("SQL manager", func() { It("lists received shares", func() { loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(1)) }) It("works with filters", func() { loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{{Type: collaboration.Filter_TYPE_EXCLUDE_DENIALS}}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{{Type: collaboration.Filter_TYPE_EXCLUDE_DENIALS}}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(1)) }) @@ -486,7 +486,7 @@ var _ = Describe("SQL manager", func() { Describe("Unshare", func() { It("deletes shares", func() { loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(1)) @@ -499,7 +499,7 @@ var _ = Describe("SQL manager", func() { Expect(err).ToNot(HaveOccurred()) loginAs(otherUser) - shares, err = mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) + shares, err = mgr.ListReceivedShares(ctx, []*collaboration.Filter{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(shares)).To(Equal(0)) }) diff --git a/pkg/share/share.go b/pkg/share/share.go index 84f66a98f94..34644a1a8f2 100644 --- a/pkg/share/share.go +++ b/pkg/share/share.go @@ -62,13 +62,13 @@ type Manager interface { // it returns only shares attached to the given resource. ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) - // ListReceivedShares returns the list of shares the user has access to. - ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) + // ListReceivedShares returns the list of shares the user has access to. `forUser` parameter for service accounts only + ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userv1beta1.UserId) ([]*collaboration.ReceivedShare, error) // GetReceivedShare returns the information for a received share. GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) - // UpdateReceivedShare updates the received share with share state. + // UpdateReceivedShare updates the received share with share state.`forUser` parameter for service accounts only UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) }