From 05758f44ea616d03641e753dc680ca2bc5666f5c Mon Sep 17 00:00:00 2001 From: jkoberg Date: Thu, 31 Aug 2023 13:52:48 +0200 Subject: [PATCH 1/5] bump reva Signed-off-by: jkoberg --- go.mod | 2 +- go.sum | 4 ++-- .../usershareprovider/usershareprovider.go | 4 +++- .../cs3org/reva/v2/pkg/cbox/share/sql/sql.go | 3 ++- .../reva/v2/pkg/share/manager/cs3/cs3.go | 11 ++++++--- .../reva/v2/pkg/share/manager/json/json.go | 22 ++++++++++------- .../v2/pkg/share/manager/jsoncs3/jsoncs3.go | 13 ++++++---- .../v2/pkg/share/manager/memory/memory.go | 24 ++++++++++++------- .../share/manager/owncloudsql/owncloudsql.go | 4 +++- .../cs3org/reva/v2/pkg/share/share.go | 2 +- vendor/modules.txt | 2 +- 11 files changed, 57 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index 049940630..4cf3eb60d 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.6.0 github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d - github.com/cs3org/reva/v2 v2.16.1-0.20230831084113-cb0897d2eefe + github.com/cs3org/reva/v2 v2.16.1-0.20230904124812-2ebd3e92cdb9 github.com/disintegration/imaging v1.6.2 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/egirna/icap-client v0.1.1 diff --git a/go.sum b/go.sum index d52099acc..c001a55c2 100644 --- a/go.sum +++ b/go.sum @@ -858,8 +858,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= -github.com/cs3org/reva/v2 v2.16.1-0.20230831084113-cb0897d2eefe h1:kCM/ZPcbG0wWDgOFrvHV/KP6d1l1PVbcPje9QfQUVAU= -github.com/cs3org/reva/v2 v2.16.1-0.20230831084113-cb0897d2eefe/go.mod h1:RvhuweTFqzezjUFU0SIdTXakrEx9vJlMvQ7znPXSP1g= +github.com/cs3org/reva/v2 v2.16.1-0.20230904124812-2ebd3e92cdb9 h1:YWkkoagYryRH56z5pw04Py0Ebg7spCZaWpsxjwGiNgU= +github.com/cs3org/reva/v2 v2.16.1-0.20230904124812-2ebd3e92cdb9/go.mod h1:RvhuweTFqzezjUFU0SIdTXakrEx9vJlMvQ7znPXSP1g= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go index 26901427d..b850b5f4d 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go @@ -301,7 +301,9 @@ func (s *service) UpdateReceivedShare(ctx context.Context, req *collaboration.Up }, nil } - share, err := s.sm.UpdateReceivedShare(ctx, req.Share, req.UpdateMask) + var uid userpb.UserId + _ = utils.ReadJSONFromOpaque(req.Opaque, "userid", &uid) + share, err := s.sm.UpdateReceivedShare(ctx, req.Share, req.UpdateMask, &uid) if err != nil { return &collaboration.UpdateReceivedShareResponse{ Status: status.NewInternal(ctx, "error updating received share"), diff --git a/vendor/github.com/cs3org/reva/v2/pkg/cbox/share/sql/sql.go b/vendor/github.com/cs3org/reva/v2/pkg/cbox/share/sql/sql.go index b9b484b9d..813a967b1 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/cbox/share/sql/sql.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/cbox/share/sql/sql.go @@ -27,6 +27,7 @@ import ( "strings" "time" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" @@ -453,7 +454,7 @@ func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareRefe } -func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, _ *userpb.UserId) (*collaboration.ReceivedShare, error) { user := ctxpkg.ContextMustGetUser(ctx) rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Share.Id}}) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/cs3/cs3.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/cs3/cs3.go index be03a0583..ed5d665c2 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/cs3/cs3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/cs3/cs3.go @@ -343,7 +343,7 @@ func (m *Manager) GetShare(ctx context.Context, ref *collaboration.ShareReferenc // check if we are the owner or the grantee user := ctxpkg.ContextMustGetUser(ctx) - if share.IsCreatedByUser(s, user) || share.IsGrantedToUser(s, user) { + if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE || share.IsCreatedByUser(s, user) || share.IsGrantedToUser(s, user) { return s, nil } @@ -617,7 +617,7 @@ func (m *Manager) GetReceivedShare(ctx context.Context, ref *collaboration.Share } // UpdateReceivedShare updates the received share with share state. -func (m *Manager) UpdateReceivedShare(ctx context.Context, rshare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *Manager) UpdateReceivedShare(ctx context.Context, rshare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userpb.UserId) (*collaboration.ReceivedShare, error) { if err := m.initialize(); err != nil { return nil, err } @@ -643,7 +643,12 @@ func (m *Manager) UpdateReceivedShare(ctx context.Context, rshare *collaboration } } - err = m.persistReceivedShare(ctx, user.Id, rs) + uid := user.GetId() + if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { + uid = forUser + } + + err = m.persistReceivedShare(ctx, uid, rs) if err != nil { return nil, err } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/json/json.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/json/json.go index 520988790..7f78e226e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/json/json.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/json/json.go @@ -542,13 +542,13 @@ func (m *mgr) getReceived(ctx context.Context, ref *collaboration.ShareReference return nil, err } user := ctxpkg.ContextMustGetUser(ctx) - if !share.IsGrantedToUser(s, user) { + if user.GetId().GetType() != userv1beta1.UserType_USER_TYPE_SERVICE && !share.IsGrantedToUser(s, user) { return nil, errtypes.NotFound(ref.String()) } return m.convert(user.Id, s), nil } -func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) { rs, err := m.getReceived(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}}) if err != nil { return nil, err @@ -568,27 +568,31 @@ func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaborat } } - user := ctxpkg.ContextMustGetUser(ctx) + u := ctxpkg.ContextMustGetUser(ctx) + uid := u.GetId().String() + if u.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + uid = forUser.String() + } // Persist state - if v, ok := m.model.State[user.Id.String()]; ok { + if v, ok := m.model.State[uid]; ok { v[rs.Share.Id.String()] = rs.State - m.model.State[user.Id.String()] = v + m.model.State[uid] = v } else { a := map[string]collaboration.ShareState{ rs.Share.Id.String(): rs.State, } - m.model.State[user.Id.String()] = a + m.model.State[uid] = a } // Persist mount point - if v, ok := m.model.MountPoint[user.Id.String()]; ok { + if v, ok := m.model.MountPoint[uid]; ok { v[rs.Share.Id.String()] = rs.MountPoint - m.model.MountPoint[user.Id.String()] = v + m.model.MountPoint[uid] = v } else { a := map[string]*provider.Reference{ rs.Share.Id.String(): rs.MountPoint, } - m.model.MountPoint[user.Id.String()] = a + m.model.MountPoint[uid] = a } if err := m.model.Save(); err != nil { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go index 661767f9c..2bfeeb0b0 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go @@ -955,7 +955,7 @@ func (m *Manager) getReceived(ctx context.Context, ref *collaboration.ShareRefer return nil, err } user := ctxpkg.ContextMustGetUser(ctx) - if !share.IsGrantedToUser(s, user) { + if user.GetId().GetType() != userv1beta1.UserType_USER_TYPE_SERVICE && !share.IsGrantedToUser(s, user) { return nil, errtypes.NotFound(ref.String()) } if share.IsExpired(s) { @@ -978,7 +978,7 @@ func (m *Manager) getReceived(ctx context.Context, ref *collaboration.ShareRefer } // UpdateReceivedShare updates the received share with share state. -func (m *Manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *Manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) { ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "UpdateReceivedShare") defer span.End() @@ -1003,10 +1003,13 @@ func (m *Manager) UpdateReceivedShare(ctx context.Context, receivedShare *collab } // write back + u := ctxpkg.ContextMustGetUser(ctx) + uid := u.GetId().GetOpaqueId() + if u.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + uid = forUser.GetOpaqueId() + } - userID := ctxpkg.ContextMustGetUser(ctx) - - err = m.UserReceivedStates.Add(ctx, userID.GetId().GetOpaqueId(), rs.Share.ResourceId.StorageId+shareid.IDDelimiter+rs.Share.ResourceId.SpaceId, rs) + err = m.UserReceivedStates.Add(ctx, uid, rs.Share.ResourceId.StorageId+shareid.IDDelimiter+rs.Share.ResourceId.SpaceId, rs) if err != nil { return nil, err } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/memory/memory.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/memory/memory.go index 4a54302c1..2719320db 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/memory/memory.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/memory/memory.go @@ -29,6 +29,7 @@ import ( "github.com/cs3org/reva/v2/pkg/share" "google.golang.org/genproto/protobuf/field_mask" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" @@ -334,7 +335,7 @@ func (m *manager) getReceived(ctx context.Context, ref *collaboration.ShareRefer user := ctxpkg.ContextMustGetUser(ctx) for _, s := range m.shares { if sharesEqual(ref, s) { - if share.IsGrantedToUser(s, user) { + if user.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE || share.IsGrantedToUser(s, user) { rs := m.convert(ctx, s) return rs, nil } @@ -343,13 +344,12 @@ func (m *manager) getReceived(ctx context.Context, ref *collaboration.ShareRefer return nil, errtypes.NotFound(ref.String()) } -func (m *manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) { rs, err := m.getReceived(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}}) if err != nil { return nil, err } - user := ctxpkg.ContextMustGetUser(ctx) m.lock.Lock() defer m.lock.Unlock() @@ -364,25 +364,31 @@ func (m *manager) UpdateReceivedShare(ctx context.Context, receivedShare *collab } } + u := ctxpkg.ContextMustGetUser(ctx) + uid := u.GetId().String() + if u.GetId().GetType() == userv1beta1.UserType_USER_TYPE_SERVICE { + uid = forUser.String() + } + // Persist state - if v, ok := m.shareState[user.Id.String()]; ok { + if v, ok := m.shareState[uid]; ok { v[rs.Share.Id] = rs.State - m.shareState[user.Id.String()] = v + m.shareState[uid] = v } else { a := map[*collaboration.ShareId]collaboration.ShareState{ rs.Share.Id: rs.State, } - m.shareState[user.Id.String()] = a + m.shareState[uid] = a } // Persist mount point - if v, ok := m.shareMountPoint[user.Id.String()]; ok { + if v, ok := m.shareMountPoint[uid]; ok { v[rs.Share.Id] = rs.MountPoint - m.shareMountPoint[user.Id.String()] = v + m.shareMountPoint[uid] = v } else { a := map[*collaboration.ShareId]*provider.Reference{ rs.Share.Id: rs.MountPoint, } - m.shareMountPoint[user.Id.String()] = a + m.shareMountPoint[uid] = a } return rs, nil diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go index edca29181..d5f28c4a1 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go @@ -27,6 +27,7 @@ import ( "strings" "time" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" @@ -422,7 +423,8 @@ func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareRefe } -func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, _ *userpb.UserId) (*collaboration.ReceivedShare, error) { + // TODO: How to inject the uid when a UserId is set? override it in the ctx? Add parameter to GetReceivedShare? rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}}) if err != nil { return nil, err diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/share.go b/vendor/github.com/cs3org/reva/v2/pkg/share/share.go index 3fb48c3b0..84f66a98f 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/share.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/share.go @@ -69,7 +69,7 @@ type Manager interface { GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) // UpdateReceivedShare updates the received share with share state. - UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) + UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, forUser *userv1beta1.UserId) (*collaboration.ReceivedShare, error) } // ReceivedShareWithUser holds the relevant information for representing a received share of a user diff --git a/vendor/modules.txt b/vendor/modules.txt index cfe678692..97b57a11c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -354,7 +354,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.16.1-0.20230831084113-cb0897d2eefe +# github.com/cs3org/reva/v2 v2.16.1-0.20230904124812-2ebd3e92cdb9 ## explicit; go 1.20 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime From 8e1b033a63071d000b22609e5200b0da5190d845 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Tue, 22 Aug 2023 11:54:45 +0200 Subject: [PATCH 2/5] auto-accept shares in frontend service Signed-off-by: jkoberg --- changelog/unreleased/auto-accept-shares.md | 5 + ocis/pkg/init/init.go | 8 +- services/frontend/README.md | 8 + services/frontend/pkg/command/events.go | 170 ++++++++++++++++++ services/frontend/pkg/command/server.go | 3 + services/frontend/pkg/config/config.go | 20 +++ .../pkg/config/defaults/defaultconfig.go | 5 + services/frontend/pkg/config/parser/parse.go | 5 + services/ocs/README.md | 9 +- .../settings/pkg/store/defaults/defaults.go | 79 ++++++++ 10 files changed, 308 insertions(+), 4 deletions(-) create mode 100644 changelog/unreleased/auto-accept-shares.md create mode 100644 services/frontend/pkg/command/events.go diff --git a/changelog/unreleased/auto-accept-shares.md b/changelog/unreleased/auto-accept-shares.md new file mode 100644 index 000000000..75ebfb699 --- /dev/null +++ b/changelog/unreleased/auto-accept-shares.md @@ -0,0 +1,5 @@ +Enhancement: Auto-Accept Shares + +Automatically accept shares when configured by the user or admin + +https://github.com/owncloud/ocis/pull/7097 diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go index 01aef0cea..b49352e91 100644 --- a/ocis/pkg/init/init.go +++ b/ocis/pkg/init/init.go @@ -73,7 +73,8 @@ type IdmService struct { } type FrontendService struct { - Archiver InsecureService + Archiver InsecureService + ServiceAccount ServiceAccount `yaml:"service_account"` } type AuthbasicService struct { @@ -377,6 +378,9 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin Notifications: Notifications{ ServiceAccount: serviceAccount, }, + Frontend: FrontendService{ + ServiceAccount: serviceAccount, + }, } if insecure { @@ -384,7 +388,7 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin cfg.AuthBearer = AuthbearerService{ AuthProviders: AuthProviderSettings{Oidc: _insecureService}, } - cfg.Frontend = FrontendService{Archiver: _insecureService} + cfg.Frontend.Archiver = _insecureService cfg.Graph.Spaces = _insecureService cfg.Graph.Events = _insecureEvents cfg.Notifications.Notifications.Events = _insecureEvents diff --git a/services/frontend/README.md b/services/frontend/README.md index abf2c3b5d..36c9b2a7b 100644 --- a/services/frontend/README.md +++ b/services/frontend/README.md @@ -55,3 +55,11 @@ The `frontend` service can use a configured store via `FRONTEND_OCS_STAT_CACHE_S 2. Though usually not necessary, a database name and a database table can be configured for event stores if the event store supports this. Generally not applicable for stores of type `in-memory`. These settings are blank by default which means that the standard settings of the configured store apply. 3. The frontend service can be scaled if not using `in-memory` stores and the stores are configured identically over all instances. 4. When using `redis-sentinel`, the Redis master to use is configured via `FRONTEND_OCS_STAT_CACHE_STORE_NODES` in the form of `:/` like `10.10.0.200:26379/mymaster`. + +## Event Handler + +The `frontend` service contains an eventhandler for handling `ocs` related events. As of now, it only listens to the `ShareCreated` event. + +### Auto-Accept Shares + +When setting the `FRONTEND_AUTO_ACCEPT_SHARES` to `true`, all incoming shares will be accepted automatically. Users can overwrite this setting individually in their profile. diff --git a/services/frontend/pkg/command/events.go b/services/frontend/pkg/command/events.go new file mode 100644 index 000000000..24141a8ad --- /dev/null +++ b/services/frontend/pkg/command/events.go @@ -0,0 +1,170 @@ +package command + +import ( + "context" + "errors" + + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/events/stream" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" + "github.com/owncloud/ocis/v2/ocis-pkg/tracing" + "github.com/owncloud/ocis/v2/services/frontend/pkg/config" + "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" + "go-micro.dev/v4/metadata" + "google.golang.org/protobuf/types/known/fieldmaskpb" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" +) + +var _registeredEvents = []events.Unmarshaller{ + events.ShareCreated{}, +} + +// ListenForEvents listens for events and acts accordingly +func ListenForEvents(cfg *config.Config, l log.Logger) { + bus, err := stream.NatsFromConfig(cfg.Service.Name, stream.NatsConfig(cfg.Events)) + if err != nil { + l.Error().Err(err).Msg("cannot connect to nats") + return + } + + evChannel, err := events.Consume(bus, "frontend", _registeredEvents...) + if err != nil { + l.Error().Err(err).Msg("cannot consume from nats") + } + + tm, err := pool.StringToTLSMode(cfg.GRPCClientTLS.Mode) + if err != nil { + return + } + + gatewaySelector, err := pool.GatewaySelector( + cfg.Reva.Address, + pool.WithTLSCACert(cfg.GRPCClientTLS.CACert), + pool.WithTLSMode(tm), + pool.WithRegistry(registry.GetRegistry()), + ) + if err != nil { + l.Error().Err(err).Msg("cannot get gateway selector") + return + } + + gwc, err := gatewaySelector.Next() + if err != nil { + l.Error().Err(err).Msg("cannot get gateway client") + return + } + + traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name) + if err != nil { + l.Error().Err(err).Msg("cannot initialize tracing") + return + } + + grpcClient, err := grpc.NewClient( + append( + grpc.GetClientOptions(cfg.GRPCClientTLS), + grpc.WithTraceProvider(traceProvider), + )..., + ) + if err != nil { + l.Error().Err(err).Msg("cannot create grpc client") + return + } + + valueService := settingssvc.NewValueService("com.owncloud.api.settings", grpcClient) + + for e := range evChannel { + switch ev := e.Event.(type) { + default: + l.Error().Interface("event", e).Msg("unhandled event") + case events.ShareCreated: + AutoAcceptShares(ev, cfg.AutoAcceptShares, l, gwc, valueService, cfg.ServiceAccount) + } + } +} + +// AutoAcceptShares automatically accepts shares if configured by the admin or user +func AutoAcceptShares(ev events.ShareCreated, autoAcceptDefault bool, l log.Logger, gwc gateway.GatewayAPIClient, vs settingssvc.ValueService, cfg config.ServiceAccount) { + ctx, err := utils.GetServiceUserContext(cfg.ServiceAccountID, gwc, cfg.ServiceAccountSecret) + if err != nil { + l.Error().Err(err).Msg("cannot impersonate user") + return + } + + uids, err := getUserIDs(ctx, gwc, ev.GranteeUserID, ev.GranteeGroupID) + if err != nil { + l.Error().Err(err).Msg("cannot get granteess") + return + } + + for _, uid := range uids { + if !autoAcceptShares(ctx, uid, autoAcceptDefault, vs) { + continue + } + + resp, err := gwc.UpdateReceivedShare(ctx, updateShareRequest(ev.ShareID, uid)) + if err != nil { + l.Error().Err(err).Msg("error sending grpc request") + continue + } + + if resp.GetStatus().GetCode() != rpc.Code_CODE_OK { + l.Error().Interface("status", resp.GetStatus()).Str("userid", uid.GetOpaqueId()).Msg("unexpected status code while accepting share") + } + } + +} + +func getUserIDs(ctx context.Context, gwc gateway.GatewayAPIClient, uid *user.UserId, gid *group.GroupId) ([]*user.UserId, error) { + if uid != nil { + return []*user.UserId{uid}, nil + } + + res, err := gwc.GetGroup(ctx, &group.GetGroupRequest{GroupId: gid}) + if err != nil { + return nil, err + } + if res.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, errors.New("could not get group") + } + + return res.GetGroup().GetMembers(), nil +} + +func autoAcceptShares(ctx context.Context, u *user.UserId, defaultValue bool, vs settingssvc.ValueService) bool { + granteeCtx := metadata.Set(ctx, middleware.AccountID, u.OpaqueId) + if resp, err := vs.GetValueByUniqueIdentifiers(granteeCtx, + &settingssvc.GetValueByUniqueIdentifiersRequest{ + AccountUuid: u.OpaqueId, + SettingId: defaults.SettingUUIDProfileAutoAcceptShares, + }, + ); err == nil { + return resp.GetValue().GetValue().GetBoolValue() + + } + return defaultValue +} + +func updateShareRequest(shareID *collaboration.ShareId, uid *user.UserId) *collaboration.UpdateReceivedShareRequest { + return &collaboration.UpdateReceivedShareRequest{ + Opaque: utils.AppendJSONToOpaque(nil, "userid", uid), + Share: &collaboration.ReceivedShare{ + Share: &collaboration.Share{ + Id: shareID, + }, + State: collaboration.ShareState_SHARE_STATE_ACCEPTED, + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}}, + } +} diff --git a/services/frontend/pkg/command/server.go b/services/frontend/pkg/command/server.go index 7308a3146..a6a813e55 100644 --- a/services/frontend/pkg/command/server.go +++ b/services/frontend/pkg/command/server.go @@ -90,6 +90,9 @@ func Server(cfg *config.Config) *cli.Command { logger.Fatal().Err(err).Msg("failed to register the http service") } + // start event handler + go ListenForEvents(cfg, logger) + return gr.Run() }, } diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go index 65d26f6cc..21acc43fc 100644 --- a/services/frontend/pkg/config/config.go +++ b/services/frontend/pkg/config/config.go @@ -53,6 +53,11 @@ type Config struct { Middleware Middleware `yaml:"middleware"` + Events Events `yaml:"events"` + GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"` + AutoAcceptShares bool `yaml:"auto_accept_shares" env:"FRONTEND_AUTO_ACCEPT_SHARES" desc:"Defines if shares should be auto accepted by default. Users can change this setting individually in their profile."` + ServiceAccount ServiceAccount `yaml:"service_account"` + Supervised bool `yaml:"-"` Context context.Context `yaml:"-"` } @@ -151,3 +156,18 @@ type Checksums struct { SupportedTypes []string `yaml:"supported_types" env:"FRONTEND_CHECKSUMS_SUPPORTED_TYPES" desc:"Define the checksum types that indicate to clients which hashes the server can use to verify upload integrity. You can provide multiple types separated by blank or comma. Supported types are 'sha1', 'md5' and 'adler32'."` PreferredUploadType string `yaml:"preferred_upload_type" env:"FRONTEND_CHECKSUMS_PREFERRED_UPLOAD_TYPE" desc:"The supported checksum type for uploads that indicates to clients supporting multiple hash algorithms which one is preferred by the server. Must be one out of the defined list of SUPPORTED_TYPES."` } + +// Events combines the configuration options for the event bus. +type Events struct { + Endpoint string `yaml:"endpoint" env:"OCIS_EVENTS_ENDPOINT;FRONTEND_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture."` + Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;FRONTEND_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` + TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;FRONTEND_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` + TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"FRONTEND_EVENTS_TLS_ROOT_CA_CERTIFICATE;OCS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;FRONTEND_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` +} + +// ServiceAccount is the configuration for the used service account +type ServiceAccount struct { + ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;FRONTEND_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."` + ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;FRONTEND_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."` +} diff --git a/services/frontend/pkg/config/defaults/defaultconfig.go b/services/frontend/pkg/config/defaults/defaultconfig.go index 39bf0c4d3..8421d30ba 100644 --- a/services/frontend/pkg/config/defaults/defaultconfig.go +++ b/services/frontend/pkg/config/defaults/defaultconfig.go @@ -120,6 +120,11 @@ func DefaultConfig() *config.Config { }, }, LDAPServerWriteEnabled: true, + Events: config.Events{ + Endpoint: "127.0.0.1:9233", + Cluster: "ocis-cluster", + EnableTLS: false, + }, } } diff --git a/services/frontend/pkg/config/parser/parse.go b/services/frontend/pkg/config/parser/parse.go index 026ae126c..3045136e3 100644 --- a/services/frontend/pkg/config/parser/parse.go +++ b/services/frontend/pkg/config/parser/parse.go @@ -5,6 +5,7 @@ import ( ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config" "github.com/owncloud/ocis/v2/ocis-pkg/shared" + "github.com/owncloud/ocis/v2/ocis-pkg/structs" "github.com/owncloud/ocis/v2/services/frontend/pkg/config" "github.com/owncloud/ocis/v2/services/frontend/pkg/config/defaults" @@ -46,5 +47,9 @@ func Validate(cfg *config.Config) error { return shared.MissingMachineAuthApiKeyError(cfg.Service.Name) } + if cfg.GRPCClientTLS == nil && cfg.Commons != nil { + cfg.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS) + } + return nil } diff --git a/services/ocs/README.md b/services/ocs/README.md index e4fe4e1bb..c755f8409 100644 --- a/services/ocs/README.md +++ b/services/ocs/README.md @@ -1,3 +1,8 @@ -# OCS +# OCS Service + +The `ocs` service (open collaboration services) serves one purpous: it has an endpoint for signing keys. + +## Signing-Keys Endpoint + +The `ocs` service contains an endpoint `/cloud/user/signing-key` on which a user can GET a signing key. Note, this functionality might be deprecated or moved in the future. -The ocs service is an ... diff --git a/services/settings/pkg/store/defaults/defaults.go b/services/settings/pkg/store/defaults/defaults.go index 96ca44acf..e8f38c24f 100644 --- a/services/settings/pkg/store/defaults/defaults.go +++ b/services/settings/pkg/store/defaults/defaults.go @@ -40,6 +40,13 @@ const ( // DisableEmailNotificationsPermissionDisplayName is the hardcoded setting name for the disable email notifications permission DisableEmailNotificationsPermissionDisplayName string = "Disable Email Notifications" + // AutoAcceptSharesPermissionID is the hardcoded setting UUID for the disable email notifications permission + AutoAcceptSharesPermissionID string = "4e41363c-a058-40a5-aec8-958897511209" + // AutoAcceptSharesPermissionName is the hardcoded setting name for the disable email notifications permission + AutoAcceptSharesPermissionName string = "AutoAcceptShares.ReadWriteDisabled" + // AutoAcceptSharesPermissionDisplayName is the hardcoded setting name for the disable email notifications permission + AutoAcceptSharesPermissionDisplayName string = "enable/disable auto accept shares" + // SetPersonalSpaceQuotaPermissionID is the hardcoded setting UUID for the set personal space quota permission SetPersonalSpaceQuotaPermissionID string = "4e6f9709-f9e7-44f1-95d4-b762d27b7896" // SetPersonalSpaceQuotaPermissionName is the hardcoded setting name for the set personal space quota permission @@ -84,6 +91,8 @@ const ( SettingUUIDProfileLanguage = "aa8cfbe5-95d4-4f7e-a032-c3c01f5f062f" // SettingUUIDProfileDisableNotifications is the hardcoded setting UUID for the disable notifications setting SettingUUIDProfileDisableNotifications = "33ffb5d6-cd07-4dc0-afb0-84f7559ae438" + // SettingUUIDProfileAutoAcceptShares is the hardcoded setting UUID for the disable notifications setting + SettingUUIDProfileAutoAcceptShares = "ec3ed4a3-3946-4efc-8f9f-76d38b12d3a9" // AccountManagementPermissionID is the hardcoded setting UUID for the account management permission AccountManagementPermissionID string = "8e587774-d929-4215-910b-a317b1e80f73" @@ -193,6 +202,21 @@ func generateBundleAdminRole() *settingsmsg.Bundle { }, }, }, + { + Id: AutoAcceptSharesPermissionID, + Name: AutoAcceptSharesPermissionName, + DisplayName: AutoAcceptSharesPermissionDisplayName, + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_SETTING, + Id: SettingUUIDProfileAutoAcceptShares, + }, + Value: &settingsmsg.Setting_PermissionValue{ + PermissionValue: &settingsmsg.Permission{ + Operation: settingsmsg.Permission_OPERATION_READWRITE, + Constraint: settingsmsg.Permission_CONSTRAINT_OWN, + }, + }, + }, { Id: AccountManagementPermissionID, Name: AccountManagementPermissionName, @@ -510,6 +534,21 @@ func generateBundleSpaceAdminRole() *settingsmsg.Bundle { }, }, }, + { + Id: AutoAcceptSharesPermissionID, + Name: AutoAcceptSharesPermissionName, + DisplayName: AutoAcceptSharesPermissionDisplayName, + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_SETTING, + Id: SettingUUIDProfileAutoAcceptShares, + }, + Value: &settingsmsg.Setting_PermissionValue{ + PermissionValue: &settingsmsg.Permission{ + Operation: settingsmsg.Permission_OPERATION_READWRITE, + Constraint: settingsmsg.Permission_CONSTRAINT_OWN, + }, + }, + }, { Id: SelfManagementPermissionID, Name: SelfManagementPermissionName, @@ -601,6 +640,21 @@ func generateBundleUserRole() *settingsmsg.Bundle { }, }, }, + { + Id: AutoAcceptSharesPermissionID, + Name: AutoAcceptSharesPermissionName, + DisplayName: AutoAcceptSharesPermissionDisplayName, + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_SETTING, + Id: SettingUUIDProfileAutoAcceptShares, + }, + Value: &settingsmsg.Setting_PermissionValue{ + PermissionValue: &settingsmsg.Permission{ + Operation: settingsmsg.Permission_OPERATION_READWRITE, + Constraint: settingsmsg.Permission_CONSTRAINT_OWN, + }, + }, + }, { Id: SelfManagementPermissionID, Name: SelfManagementPermissionName, @@ -692,6 +746,21 @@ func generateBundleUserLightRole() *settingsmsg.Bundle { }, }, }, + { + Id: AutoAcceptSharesPermissionID, + Name: AutoAcceptSharesPermissionName, + DisplayName: AutoAcceptSharesPermissionDisplayName, + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_SETTING, + Id: SettingUUIDProfileAutoAcceptShares, + }, + Value: &settingsmsg.Setting_PermissionValue{ + PermissionValue: &settingsmsg.Permission{ + Operation: settingsmsg.Permission_OPERATION_READWRITE, + Constraint: settingsmsg.Permission_CONSTRAINT_OWN, + }, + }, + }, }, } } @@ -727,6 +796,16 @@ func generateBundleProfileRequest() *settingsmsg.Bundle { }, Value: &settingsmsg.Setting_BoolValue{BoolValue: &settingsmsg.Bool{Default: false, Label: "disable notifications"}}, }, + { + Id: SettingUUIDProfileAutoAcceptShares, + Name: "auto-accept-shares", + DisplayName: "Auto accept shares", + Description: "Automatically accept shares", + Resource: &settingsmsg.Resource{ + Type: settingsmsg.Resource_TYPE_USER, + }, + Value: &settingsmsg.Setting_BoolValue{BoolValue: &settingsmsg.Bool{Default: true, Label: "auto accept shares"}}, + }, }, } } From de14367d16c86ce1df9a4e4280e592bdaf1df192 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 6 Sep 2023 16:46:43 +0200 Subject: [PATCH 3/5] start eventlistener in group Signed-off-by: jkoberg --- services/frontend/pkg/command/events.go | 33 +++++++++++++++---------- services/frontend/pkg/command/server.go | 8 ++++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/services/frontend/pkg/command/events.go b/services/frontend/pkg/command/events.go index 24141a8ad..7a4ba1983 100644 --- a/services/frontend/pkg/command/events.go +++ b/services/frontend/pkg/command/events.go @@ -31,21 +31,22 @@ var _registeredEvents = []events.Unmarshaller{ } // ListenForEvents listens for events and acts accordingly -func ListenForEvents(cfg *config.Config, l log.Logger) { +func ListenForEvents(ctx context.Context, cfg *config.Config, l log.Logger) error { bus, err := stream.NatsFromConfig(cfg.Service.Name, stream.NatsConfig(cfg.Events)) if err != nil { l.Error().Err(err).Msg("cannot connect to nats") - return + return err } evChannel, err := events.Consume(bus, "frontend", _registeredEvents...) if err != nil { l.Error().Err(err).Msg("cannot consume from nats") + return err } tm, err := pool.StringToTLSMode(cfg.GRPCClientTLS.Mode) if err != nil { - return + return err } gatewaySelector, err := pool.GatewaySelector( @@ -56,19 +57,19 @@ func ListenForEvents(cfg *config.Config, l log.Logger) { ) if err != nil { l.Error().Err(err).Msg("cannot get gateway selector") - return + return err } gwc, err := gatewaySelector.Next() if err != nil { l.Error().Err(err).Msg("cannot get gateway client") - return + return err } traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name) if err != nil { l.Error().Err(err).Msg("cannot initialize tracing") - return + return err } grpcClient, err := grpc.NewClient( @@ -79,17 +80,23 @@ func ListenForEvents(cfg *config.Config, l log.Logger) { ) if err != nil { l.Error().Err(err).Msg("cannot create grpc client") - return + return err } valueService := settingssvc.NewValueService("com.owncloud.api.settings", grpcClient) - for e := range evChannel { - switch ev := e.Event.(type) { - default: - l.Error().Interface("event", e).Msg("unhandled event") - case events.ShareCreated: - AutoAcceptShares(ev, cfg.AutoAcceptShares, l, gwc, valueService, cfg.ServiceAccount) + for { + select { + case e := <-evChannel: + switch ev := e.Event.(type) { + default: + l.Error().Interface("event", e).Msg("unhandled event") + case events.ShareCreated: + AutoAcceptShares(ev, cfg.AutoAcceptShares, l, gwc, valueService, cfg.ServiceAccount) + } + case <-ctx.Done(): + l.Info().Msg("context cancelled") + return ctx.Err() } } } diff --git a/services/frontend/pkg/command/server.go b/services/frontend/pkg/command/server.go index a6a813e55..1aa8a72ac 100644 --- a/services/frontend/pkg/command/server.go +++ b/services/frontend/pkg/command/server.go @@ -90,8 +90,12 @@ func Server(cfg *config.Config) *cli.Command { logger.Fatal().Err(err).Msg("failed to register the http service") } - // start event handler - go ListenForEvents(cfg, logger) + // add event handler + gr.Add(func() error { + return ListenForEvents(ctx, cfg, logger) + }, func(_ error) { + cancel() + }) return gr.Run() }, From add4dec96355232b5f5a10f5aa4cfafbbbbb7dbc Mon Sep 17 00:00:00 2001 From: kobergj Date: Wed, 6 Sep 2023 17:24:11 +0200 Subject: [PATCH 4/5] Update services/ocs/README.md Co-authored-by: Martin --- services/ocs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/ocs/README.md b/services/ocs/README.md index c755f8409..965770087 100644 --- a/services/ocs/README.md +++ b/services/ocs/README.md @@ -1,6 +1,6 @@ # OCS Service -The `ocs` service (open collaboration services) serves one purpous: it has an endpoint for signing keys. +The `ocs` service (open collaboration services) serves one purpose: it has an endpoint for signing keys which the web frontend accesses when uploading data. ## Signing-Keys Endpoint From 6c9f0cc6d8b711e3aaf77640ea3e250d82b9556d Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 7 Sep 2023 07:13:44 +0200 Subject: [PATCH 5/5] chore: fix codacy md last line annotation --- services/ocs/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/services/ocs/README.md b/services/ocs/README.md index 965770087..6425f82a6 100644 --- a/services/ocs/README.md +++ b/services/ocs/README.md @@ -5,4 +5,3 @@ The `ocs` service (open collaboration services) serves one purpose: it has an en ## Signing-Keys Endpoint The `ocs` service contains an endpoint `/cloud/user/signing-key` on which a user can GET a signing key. Note, this functionality might be deprecated or moved in the future. -