From 68ead430005d99592f9f158744188fd6375ade75 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 20 Dec 2022 14:15:36 +0100 Subject: [PATCH] extract code to methods --- services/notifications/pkg/service/service.go | 274 ++++-------------- services/notifications/pkg/service/spaces.go | 123 ++++++++ 2 files changed, 183 insertions(+), 214 deletions(-) create mode 100644 services/notifications/pkg/service/spaces.go diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 13a861297..2b3c55b44 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "net/url" "os" "os/signal" @@ -15,7 +16,6 @@ import ( providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/events" - "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/notifications/pkg/channels" "github.com/owncloud/ocis/v2/services/notifications/pkg/email" @@ -80,233 +80,33 @@ func (s eventsNotifier) Run() error { } } -func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { - sharerUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ - UserId: e.Creator, - }) - if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("Could not get user response from gatway client") - return - } - - // Get auth context - ownerCtx := revactx.ContextSetUser(context.Background(), sharerUserResponse.User) - authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ - Type: "machine", - ClientId: "userid:" + e.Executant.OpaqueId, - ClientSecret: s.machineAuthAPIKey, - }) - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("Could not impersonate sharer") - return - } - - if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("could not get authenticated context for user") - return - } - ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, revactx.TokenHeader, authRes.Token) - - resourceID, err := storagespace.ParseID(e.ID.OpaqueId) - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Str("itemid", e.ID.OpaqueId). - Msg("could not parse resourceid from ItemID ") - return - } - // TODO: maybe cache this stat to reduce storage iops - md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{ - Ref: &providerv1beta1.Reference{ - ResourceId: &resourceID, - }, - // TODO: this filter needs to be implemented - //FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space.name"}}, - }) - - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Str("itemid", e.ID.OpaqueId). - Msg("could not stat resource") - return - } - - if md.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Str("itemid", e.ID.OpaqueId). - Str("rpc status", md.Status.Code.String()). - Msg("could not stat resource") - return - } - - shareLink, err := urlJoinPath(s.ocisURL, "f", e.ID.OpaqueId) - - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("could not create link to the share") - return - } - spaceGrantee := "" - switch { - // Note: We're using the 'ownerCtx' (authenticated as the share owner) here for requesting - // the Grantees of the shares. Ideally the notfication service would use some kind of service - // user for this. - case e.GranteeUserID != nil: - granteeUserResponse, err := s.gwClient.GetUser(ownerCtx, &userv1beta1.GetUserRequest{ - UserId: e.GranteeUserID, - }) - if err != nil || granteeUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("Could not get user response from gatway client") - return - } - spaceGrantee = granteeUserResponse.GetUser().DisplayName - case e.GranteeGroupID != nil: - granteeGroupResponse, err := s.gwClient.GetGroup(ownerCtx, &groupv1beta1.GetGroupRequest{ - GroupId: e.GranteeGroupID, - }) - if err != nil || granteeGroupResponse.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("Could not get group response from gatway client") - return - } - spaceGrantee = granteeGroupResponse.GetGroup().DisplayName - default: - s.logger.Error(). - Str("event", "ShareCreated"). - Msg("Event 'ShareCreated' has no grantee") - return - } - - sharerDisplayName := sharerUserResponse.GetUser().DisplayName - msg, err := email.RenderEmailTemplate("spaces/sharedSpace.email.body.tmpl", map[string]string{ - "SpaceGrantee": spaceGrantee, - "SpaceSharer": sharerDisplayName, - "SpaceName": md.GetInfo().GetSpace().Name, - "ShareLink": shareLink, - }, s.emailTemplatePath) - - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("Could not render E-Mail body template for spaces") - } - - emailSubject, err := email.RenderEmailTemplate("spaces/sharedSpace.email.subject.tmpl", map[string]string{ - "SpaceSharer": sharerDisplayName, - "SpaceName": md.GetInfo().GetSpace().Name, - }, s.emailTemplatePath) - - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("Could not render E-Mail subject template for spaces") - } - - if e.GranteeUserID != nil { - err = s.channel.SendMessage(ownerCtx, []string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName) - } else if e.GranteeGroupID != nil { - err = s.channel.SendMessageToGroup(ownerCtx, e.GranteeGroupID, msg, emailSubject, sharerDisplayName) - } - if err != nil { - s.logger.Error(). - Err(err). - Str("event", "SpaceCreated"). - Msg("failed to send a message") - } -} - func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { - sharerUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ - UserId: e.Sharer, - }) - if err != nil || sharerUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("Could not get user response from gatway client") - return - } + logger := s.logger.With(). + Str("event", "ShareCreated"). + Str("itemid", e.ItemID.OpaqueId). + Logger() - // Get auth context - ownerCtx := revactx.ContextSetUser(context.Background(), sharerUserResponse.User) - authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ - Type: "machine", - ClientId: "userid:" + e.Sharer.OpaqueId, - ClientSecret: s.machineAuthAPIKey, - }) + impersonateRes, err := s.impersonate(e.Sharer) if err != nil { - s.logger.Error(). + logger.Error(). Err(err). - Str("event", "ShareCreated"). Msg("Could not impersonate sharer") return } + ownerCtx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, impersonateRes.Token) - if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Msg("could not get authenticated context for user") - return - } - ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, revactx.TokenHeader, authRes.Token) - - // TODO: maybe cache this stat to reduce storage iops - md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{ - Ref: &providerv1beta1.Reference{ - ResourceId: e.ItemID, - }, - FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, - }) - + resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}}) if err != nil { - s.logger.Error(). + logger.Error(). Err(err). - Str("event", "ShareCreated"). - Str("itemid", e.ItemID.OpaqueId). - Msg("could not stat resource") - return - } - - if md.Status.Code != rpcv1beta1.Code_CODE_OK { - s.logger.Error(). - Err(err). - Str("event", "ShareCreated"). - Str("itemid", e.ItemID.OpaqueId). - Str("rpc status", md.Status.Code.String()). Msg("could not stat resource") return } shareLink, err := urlJoinPath(s.ocisURL, "files/shares/with-me") - if err != nil { - s.logger.Error(). + logger.Error(). Err(err). - Str("event", "ShareCreated"). Msg("could not create link to the share") return } @@ -347,11 +147,11 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { return } - sharerDisplayName := sharerUserResponse.GetUser().DisplayName + sharerDisplayName := impersonateRes.GetUser().DisplayName msg, err := email.RenderEmailTemplate("shares/shareCreated.email.body.tmpl", map[string]string{ "ShareGrantee": shareGrantee, "ShareSharer": sharerDisplayName, - "ShareFolder": md.GetInfo().Name, + "ShareFolder": resourceInfo.Name, "ShareLink": shareLink, }, s.emailTemplatePath) @@ -364,7 +164,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { emailSubject, err := email.RenderEmailTemplate("shares/shareCreated.email.subject.tmpl", map[string]string{ "ShareSharer": sharerDisplayName, - "ShareFolder": md.GetInfo().Name, + "ShareFolder": resourceInfo.Name, }, s.emailTemplatePath) if err != nil { @@ -387,6 +187,33 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { } } +func (s eventsNotifier) impersonate(userID *userv1beta1.UserId) (*gateway.AuthenticateResponse, error) { + getUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{ + UserId: userID, + }) + if err != nil { + return nil, err + } + if getUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + return nil, fmt.Errorf("error getting user: %s", getUserResponse.Status.Message) + } + + // Get auth context + ownerCtx := revactx.ContextSetUser(context.Background(), getUserResponse.User) + authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + userID.OpaqueId, + ClientSecret: s.machineAuthAPIKey, + }) + if err != nil { + return nil, err + } + if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { + return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message) + } + return authRes, nil +} + // TODO: this function is a backport for go1.19 url.JoinPath, upon go bump, replace this func urlJoinPath(base string, elements ...string) (string, error) { u, err := url.Parse(base) @@ -396,3 +223,22 @@ func urlJoinPath(base string, elements ...string) (string, error) { u.Path = path.Join(append([]string{u.Path}, elements...)...) return u.String(), nil } + +func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *providerv1beta1.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*providerv1beta1.ResourceInfo, error) { + // TODO: maybe cache this stat to reduce storage iops + md, err := s.gwClient.Stat(ctx, &providerv1beta1.StatRequest{ + Ref: &providerv1beta1.Reference{ + ResourceId: resourceID, + }, + FieldMask: fieldmask, + }) + + if err != nil { + return nil, err + } + + if md.Status.Code != rpcv1beta1.Code_CODE_OK { + return nil, fmt.Errorf("could not resource info: %s", md.Status.Message) + } + return md.GetInfo(), nil +} diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go new file mode 100644 index 000000000..790e24ca4 --- /dev/null +++ b/services/notifications/pkg/service/spaces.go @@ -0,0 +1,123 @@ +package service + +import ( + "context" + + groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/owncloud/ocis/v2/services/notifications/pkg/email" + "google.golang.org/grpc/metadata" +) + +func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { + logger := s.logger.With(). + Str("event", "SpaceShared"). + Str("itemid", e.ID.OpaqueId). + Logger() + + impersonateRes, err := s.impersonate(e.Executant) + if err != nil { + logger.Error(). + Err(err). + Msg("could not handle space shared event") + return + } + ownerCtx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, impersonateRes.Token) + + resourceID, err := storagespace.ParseID(e.ID.OpaqueId) + if err != nil { + logger.Error(). + Err(err). + Msg("could not parse resourceid from ItemID ") + return + } + + resourceInfo, err := s.getResourceInfo(ownerCtx, &resourceID, nil) + if err != nil { + logger.Error(). + Err(err). + Msg("could not get space info") + return + } + + shareLink, err := urlJoinPath(s.ocisURL, "f", e.ID.OpaqueId) + if err != nil { + logger.Error(). + Err(err). + Msg("could not create link to the share") + return + } + + spaceGrantee := "" + switch { + // Note: We're using the 'ownerCtx' (authenticated as the share owner) here for requesting + // the Grantees of the shares. Ideally the notfication service would use some kind of service + // user for this. + case e.GranteeUserID != nil: + granteeUserResponse, err := s.gwClient.GetUser(ownerCtx, &userv1beta1.GetUserRequest{ + UserId: e.GranteeUserID, + }) + if err != nil || granteeUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + logger.Error(). + Err(err). + Msg("Could not get user response from gatway client") + return + } + spaceGrantee = granteeUserResponse.GetUser().DisplayName + case e.GranteeGroupID != nil: + granteeGroupResponse, err := s.gwClient.GetGroup(ownerCtx, &groupv1beta1.GetGroupRequest{ + GroupId: e.GranteeGroupID, + }) + if err != nil || granteeGroupResponse.Status.Code != rpcv1beta1.Code_CODE_OK { + logger.Error(). + Err(err). + Msg("Could not get group response from gatway client") + return + } + spaceGrantee = granteeGroupResponse.GetGroup().DisplayName + default: + logger.Error(). + Msg("Event 'SpaceShared' has no grantee") + return + } + + sharerDisplayName := impersonateRes.GetUser().DisplayName + msg, err := email.RenderEmailTemplate("spaces/sharedSpace.email.body.tmpl", map[string]string{ + "SpaceGrantee": spaceGrantee, + "SpaceSharer": sharerDisplayName, + "SpaceName": resourceInfo.GetSpace().Name, + "ShareLink": shareLink, + }, s.emailTemplatePath) + + if err != nil { + logger.Error(). + Err(err). + Msg("Could not render E-Mail body template for spaces") + } + + emailSubject, err := email.RenderEmailTemplate("spaces/sharedSpace.email.subject.tmpl", map[string]string{ + "SpaceSharer": sharerDisplayName, + "SpaceName": resourceInfo.GetSpace().Name, + }, s.emailTemplatePath) + + if err != nil { + logger.Error(). + Err(err). + Msg("Could not render E-Mail subject template for spaces") + } + + if e.GranteeUserID != nil { + err = s.channel.SendMessage(ownerCtx, []string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName) + } else if e.GranteeGroupID != nil { + err = s.channel.SendMessageToGroup(ownerCtx, e.GranteeGroupID, msg, emailSubject, sharerDisplayName) + } + if err != nil { + logger.Error(). + Err(err). + Msg("failed to send a message") + } +}