diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index 2b66e42a35..74eda00e13 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -2,7 +2,6 @@ package service import ( "bytes" - "context" "errors" "text/template" "time" @@ -11,10 +10,13 @@ import ( storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/utils" + ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ) var ( _resourceTypeSpace = "storagespace" + _resourceTypeShare = "share" ) // OC10Notification is the oc10 style representation of an event @@ -33,55 +35,57 @@ type OC10Notification struct { MessageDetails map[string]interface{} `json:"messageRichParameters"` } -// SpaceDisabled converts a SpaceDisabled event to an OC10Notification -func (ul *UserlogService) SpaceDisabled(ctx context.Context, eventid string, ev events.SpaceDisabled) (OC10Notification, error) { - user, err := ul.getUser(ctx, ev.Executant) - if err != nil { - return OC10Notification{}, err +// ConvertEvent converts an eventhistory event to an OC10Notification +func (ul *UserlogService) ConvertEvent(event *ehmsg.Event) (OC10Notification, error) { + etype, ok := ul.registeredEvents[event.Type] + if !ok { + // this should not happen + return OC10Notification{}, errors.New("eventtype not registered") } - space, err := ul.getSpace(ul.impersonate(user.GetId()), ev.ID.GetOpaqueId()) + einterface, err := etype.Unmarshal(event.Event) if err != nil { - return OC10Notification{}, err + // this shouldn't happen either + return OC10Notification{}, errors.New("cant unmarshal event") } - subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceDisabled, map[string]string{ - "username": user.GetDisplayName(), - "spacename": space.GetName(), - }) - if err != nil { - return OC10Notification{}, err + switch ev := einterface.(type) { + default: + return OC10Notification{}, errors.New("unknown event type") + // space related + case events.SpaceDisabled: + return ul.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId()) + case events.SpaceDeleted: + return ul.spaceMessage(event.Id, SpaceDeleted, ev.Executant, ev.ID.GetOpaqueId()) + case events.SpaceShared: + return ul.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId()) + case events.SpaceUnshared: + return ul.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId()) + case events.SpaceMembershipExpired: + return ul.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId()) + + // share related + case events.ShareCreated: + return ul.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID) + case events.ShareExpired: + return ul.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID) + case events.ShareRemoved: + return ul.shareMessage(event.Id, ShareRemoved, ev.Executant, nil) } - - return OC10Notification{ - EventID: eventid, - Service: ul.cfg.Service.Name, - UserName: user.GetUsername(), - Timestamp: time.Now().Format(time.RFC3339Nano), - ResourceID: ev.ID.GetOpaqueId(), - ResourceType: _resourceTypeSpace, - Subject: subj, - SubjectRaw: subjraw, - Message: msg, - MessageRaw: msgraw, - MessageDetails: ul.getDetails(user, space, nil), - }, nil - } -// SpaceShared converts a SpaceShared event to an OC10Notification -func (ul *UserlogService) SpaceShared(ctx context.Context, eventid string, ev events.SpaceShared) (OC10Notification, error) { - user, err := ul.getUser(ctx, ev.Executant) +func (ul *UserlogService) spaceMessage(eventid string, eventname string, executant *user.UserId, spaceid string) (OC10Notification, error) { + ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) if err != nil { return OC10Notification{}, err } - space, err := ul.getSpace(ul.impersonate(user.GetId()), ev.ID.GetOpaqueId()) + space, err := ul.getSpace(ctx, spaceid) if err != nil { return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceShared, map[string]string{ + subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{ "username": user.GetDisplayName(), "spacename": space.GetName(), }) @@ -94,7 +98,7 @@ func (ul *UserlogService) SpaceShared(ctx context.Context, eventid string, ev ev Service: ul.cfg.Service.Name, UserName: user.GetUsername(), Timestamp: time.Now().Format(time.RFC3339Nano), - ResourceID: ev.ID.GetOpaqueId(), + ResourceID: spaceid, ResourceType: _resourceTypeSpace, Subject: subj, SubjectRaw: subjraw, @@ -102,7 +106,40 @@ func (ul *UserlogService) SpaceShared(ctx context.Context, eventid string, ev ev MessageRaw: msgraw, MessageDetails: ul.getDetails(user, space, nil), }, nil +} +func (ul *UserlogService) shareMessage(eventid string, eventname string, executant *user.UserId, resourceid *storageprovider.ResourceId) (OC10Notification, error) { + ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey) + if err != nil { + return OC10Notification{}, err + } + + info, err := ul.getResource(ctx, resourceid) + if err != nil { + return OC10Notification{}, err + } + + subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{ + "username": user.GetDisplayName(), + "resourcename": info.GetName(), + }) + if err != nil { + return OC10Notification{}, err + } + + return OC10Notification{ + EventID: eventid, + Service: ul.cfg.Service.Name, + UserName: user.GetUsername(), + Timestamp: time.Now().Format(time.RFC3339Nano), + ResourceID: storagespace.FormatResourceID(*info.GetId()), + ResourceType: _resourceTypeShare, + Subject: subj, + SubjectRaw: subjraw, + Message: msg, + MessageRaw: msgraw, + MessageDetails: ul.getDetails(user, nil, info), + }, nil } func (ul *UserlogService) composeMessage(eventname string, vars map[string]string) (string, string, string, string, error) { @@ -114,17 +151,17 @@ func (ul *UserlogService) composeMessage(eventname string, vars map[string]strin subject := ul.executeTemplate(tpl.Subject, vars) subjectraw := ul.executeTemplate(tpl.Subject, map[string]string{ - "username": "{user}", - "spacename": "{space}", - "resource": "{resource}", + "username": "{user}", + "spacename": "{space}", + "resourcename": "{resource}", }) message := ul.executeTemplate(tpl.Message, vars) messageraw := ul.executeTemplate(tpl.Message, map[string]string{ - "username": "{user}", - "spacename": "{space}", - "resource": "{resource}", + "username": "{user}", + "spacename": "{space}", + "resourcename": "{resource}", }) return subject, subjectraw, message, messageraw, nil diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index b9e7ef0398..072e35b26b 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -1,17 +1,10 @@ package service import ( - "context" "encoding/json" - "errors" - "fmt" "net/http" - "time" - storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" - "github.com/cs3org/reva/v2/pkg/events" - ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ) // ServeHTTP fulfills Handler interface @@ -37,7 +30,7 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request resp := GetEventResponseOC10{} for _, e := range evs { - noti, err := ul.convertEvent(r.Context(), e) + noti, err := ul.ConvertEvent(e) if err != nil { ul.log.Error().Err(err).Str("eventid", e.Id).Str("eventtype", e.Type).Msg("failed to convert event") continue @@ -76,59 +69,6 @@ func (ul *UserlogService) HandleDeleteEvents(w http.ResponseWriter, r *http.Requ w.WriteHeader(http.StatusOK) } -func (ul *UserlogService) convertEvent(ctx context.Context, event *ehmsg.Event) (OC10Notification, error) { - etype, ok := ul.registeredEvents[event.Type] - if !ok { - // this should not happen - return OC10Notification{}, errors.New("eventtype not registered") - } - - einterface, err := etype.Unmarshal(event.Event) - if err != nil { - // this shouldn't happen either - return OC10Notification{}, errors.New("cant unmarshal event") - } - - noti := OC10Notification{ - EventID: event.Id, - Service: "userlog", - Timestamp: time.Now().Format(time.RFC3339Nano), - } - - // TODO: strange bug with getting space -> fix postponed to make master panic-free - var space storageprovider.StorageSpace - - switch ev := einterface.(type) { - // space related - case events.SpaceDisabled: - return ul.SpaceDisabled(ctx, event.Id, ev) - case events.SpaceDeleted: - noti.Subject = "Space deleted" - noti.Message = fmt.Sprintf("Space '%s' was deleted", space.Name) - case events.SpaceShared: - return ul.SpaceShared(ctx, event.Id, ev) - case events.SpaceUnshared: - noti.Subject = "Space unshared" - noti.Message = fmt.Sprintf("Space '%s' was unshared", space.Name) - case events.SpaceMembershipExpired: - noti.Subject = "Space membership expired" - noti.Message = fmt.Sprintf("A spacemembership for space '%s' has expired", space.Name) - - // share related - case events.ShareCreated: - noti.Subject = "Share received" - noti.Message = fmt.Sprintf("A file was shared in space %s", space.Name) - case events.ShareExpired: - noti.Subject = "Share expired" - noti.Message = fmt.Sprintf("A share has expired in space %s", space.Name) - case events.ShareRemoved: - noti.Subject = "Share removed" - noti.Message = "share was removed" - } - - return noti, nil -} - // GetEventResponseOC10 is the response from GET events endpoint in oc10 style type GetEventResponseOC10 struct { OCS struct { diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index a0d1de45aa..657a306597 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -395,6 +395,19 @@ func (ul *UserlogService) getGroup(ctx context.Context, groupid string) (*group. return r.GetGroup(), nil } +func (ul *UserlogService) getResource(ctx context.Context, resourceid *storageprovider.ResourceId) (*storageprovider.ResourceInfo, error) { + res, err := ul.gwClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}}) + if err != nil { + return nil, err + } + + if res.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, fmt.Errorf("Unexpected status code while getting space: %v", res.GetStatus().GetCode()) + } + + return res.GetInfo(), nil +} + func listStorageSpaceRequest(spaceID string) *storageprovider.ListStorageSpacesRequest { return &storageprovider.ListStorageSpacesRequest{ Filters: []*storageprovider.ListStorageSpacesRequest_Filter{ diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index cd83bb74b3..3a597cfb6d 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -4,13 +4,51 @@ import "text/template" // the available templates var ( + SpaceShared = "space-shared" + SpaceSharedSubject = "Space shared" + SpaceSharedMessage = "{{ .username }} shared Space {{ .spacename }} with you" + + SpaceUnshared = "space-unshared" + SpaceUnsharedSubject = "Removed from Space" + SpaceUnsharedMessage = "{{ .username }} removed you from Space {{ .spacename }}" + SpaceDisabled = "space-disabled" SpaceDisabledSubject = "Space disabled" SpaceDisabledMessage = "{{ .username }} disabled Space {{ .spacename }}" - SpaceShared = "space-shared" - SpaceSharedSubject = "Space shared" - SpaceSharedMessage = "{{ .username }} shared Space {{ .spacename }} with you" + SpaceDeleted = "space-deleted" + SpaceDeletedSubject = "Space deleted" + SpaceDeletedMessage = "{{ .username }} deleted Space {{ .spacename }}" + + SpaceMembershipExpired = "space-membership-expired" + SpaceMembershipExpiredSubject = "Membership expired" + SpaceMembershipExpiredMessage = "Access to Space {{ .spacename }} lost" + + ShareCreated = "item-shared" + ShareCreatedSubject = "Resource shared" + ShareCreatedMessage = "{{ .username }} shared {{ .itemname }} with you" + + ShareRemoved = "item-unshared" + ShareRemovedSubject = "Resource unshared" + ShareRemovedMessage = "{{ .username }} unshared {{ .itemname }} with you" + + ShareExpired = "share-expired" + ShareExpiredSubject = "Share expired" + ShareExpiredMessage = "Access to {{ .resourcename }} expired" +) + +// rendered templates +var ( + _templates = map[string]NotificationTemplate{ + SpaceShared: notiTmpl(SpaceSharedSubject, SpaceSharedMessage), + SpaceUnshared: notiTmpl(SpaceUnsharedSubject, SpaceUnsharedMessage), + SpaceDisabled: notiTmpl(SpaceDisabledSubject, SpaceDisabledMessage), + SpaceDeleted: notiTmpl(SpaceDeletedSubject, SpaceDeletedMessage), + SpaceMembershipExpired: notiTmpl(SpaceMembershipExpiredSubject, SpaceMembershipExpiredMessage), + ShareCreated: notiTmpl(ShareCreatedSubject, ShareCreatedMessage), + ShareRemoved: notiTmpl(ShareRemovedSubject, ShareRemovedMessage), + ShareExpired: notiTmpl(ShareExpiredSubject, ShareExpiredMessage), + } ) // NotificationTemplate is the data structure for the notifications @@ -19,16 +57,9 @@ type NotificationTemplate struct { Message *template.Template } -// rendered templates -var ( - _templates = map[string]NotificationTemplate{ - SpaceDisabled: { - Subject: template.Must(template.New("").Parse(SpaceDisabledSubject)), - Message: template.Must(template.New("").Parse(SpaceDisabledMessage)), - }, - SpaceShared: { - Subject: template.Must(template.New("").Parse(SpaceSharedSubject)), - Message: template.Must(template.New("").Parse(SpaceSharedMessage)), - }, +func notiTmpl(subjectname string, messagename string) NotificationTemplate { + return NotificationTemplate{ + Subject: template.Must(template.New("").Parse(subjectname)), + Message: template.Must(template.New("").Parse(messagename)), } -) +}