From c7952c247dd370117a9a3cb8adbf28e246175c29 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 11 Mar 2022 12:14:46 +0100 Subject: [PATCH 01/10] share updated event Signed-off-by: jkoberg --- audit/pkg/service/service.go | 17 ++++++++ audit/pkg/types/constants.go | 20 +++++++++ audit/pkg/types/conversion.go | 82 ++++++++++++++++++++++++----------- audit/pkg/types/events.go | 1 + audit/pkg/types/types.go | 14 ++++++ go.mod | 4 +- go.sum | 4 +- 7 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 audit/pkg/types/constants.go diff --git a/audit/pkg/service/service.go b/audit/pkg/service/service.go index 4f1f4b1aa..31bb76078 100644 --- a/audit/pkg/service/service.go +++ b/audit/pkg/service/service.go @@ -45,6 +45,8 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger switch ev := i.(type) { case events.ShareCreated: auditEvent = types.ShareCreated(ev) + case events.ShareUpdated: + auditEvent = types.ShareUpdated(ev) default: log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev)) continue @@ -95,5 +97,20 @@ func Marshal(format string, log log.Logger) Marshaller { return nil case "json": return json.Marshal + case "minimal": + return func(ev interface{}) ([]byte, error) { + b, err := json.Marshal(ev) + if err != nil { + return nil, err + } + + m := make(map[string]interface{}) + if err := json.Unmarshal(b, &m); err != nil { + return nil, err + } + + format := fmt.Sprintf("%s)\n %s", m["Action"], m["Message"]) + return []byte(format), nil + } } } diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go new file mode 100644 index 000000000..33dd335f1 --- /dev/null +++ b/audit/pkg/types/constants.go @@ -0,0 +1,20 @@ +package types + +import "fmt" + +// short identifiers for audit actions +const ( + ActionShareCreated = "share_created" + ActionSharePermissionUpdated = "share_permission_updated" + ActionShareDisplayNameUpdated = "share_name_updated" +) + +// MessageShareCreated returns the human readable string that describes the action +func MessageShareCreated(sharer, item, grantee string) string { + return fmt.Sprintf("user '%s' shared file '%s' with '%s'", sharer, item, grantee) +} + +// MessageShareUpdated returns the human readable string that describes the action +func MessageShareUpdated(sharer, shareID, fieldUpdated string) string { + return fmt.Sprintf("user '%s' updated field '%s' of share '%s'", sharer, fieldUpdated, shareID) +} diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index c4ab66f5e..e939310b7 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -1,20 +1,13 @@ package types import ( - "fmt" "time" "github.com/cs3org/reva/v2/pkg/events" -) -// actions -const ( - actionShareCreated = "file_shared" -) - -// messages -const ( - messageShareCreated = "user '%s' shared file '%s' with '%s'" + group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" ) // BasicAuditEvent creates an AuditEvent from given values @@ -37,39 +30,30 @@ func BasicAuditEvent(uid string, ctime string, msg string, action string) AuditE } // SharingAuditEvent creates an AuditEventSharing from given values -func SharingAuditEvent(fileid string, uid string, base AuditEvent) AuditEventSharing { +func SharingAuditEvent(shareid string, fileid string, uid string, base AuditEvent) AuditEventSharing { return AuditEventSharing{ AuditEvent: base, FileID: fileid, Owner: uid, + ShareID: shareid, // NOTE: those values are not in the events and can therefore not be filled at the moment - ShareID: "", - Path: "", + Path: "", } } // ShareCreated converts a ShareCreated Event to an AuditEventShareCreated func ShareCreated(ev events.ShareCreated) AuditEventShareCreated { - with := "" - typ := "" - if ev.GranteeUserID != nil && ev.GranteeUserID.OpaqueId != "" { - with = ev.GranteeUserID.OpaqueId - typ = "user" - } else if ev.GranteeGroupID != nil && ev.GranteeGroupID.OpaqueId != "" { - with = ev.GranteeGroupID.OpaqueId - typ = "group" - } uid := ev.Sharer.OpaqueId - t := time.Unix(int64(ev.CTime.Seconds), int64(ev.CTime.Nanos)).Format(time.RFC3339) - base := BasicAuditEvent(uid, t, fmt.Sprintf(messageShareCreated, uid, ev.ItemID.OpaqueId, with), actionShareCreated) + with, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID) + base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageShareCreated(uid, ev.ItemID.OpaqueId, with), ActionShareCreated) return AuditEventShareCreated{ - AuditEventSharing: SharingAuditEvent(ev.ItemID.OpaqueId, uid, base), + AuditEventSharing: SharingAuditEvent("", ev.ItemID.OpaqueId, uid, base), ShareOwner: uid, ShareWith: with, ShareType: typ, - // NOTE: those values are not in the events and can therefore not be filled at the moment + // NOTE: those values are not in the event and can therefore not be filled at the moment ItemType: "", ExpirationDate: "", SharePass: false, @@ -77,3 +61,49 @@ func ShareCreated(ev events.ShareCreated) AuditEventShareCreated { ShareToken: "", } } + +// ShareUpdated converts a ShareUpdated event to an AuditEventShareUpdated +func ShareUpdated(ev events.ShareUpdated) AuditEventShareUpdated { + uid := ev.Sharer.OpaqueId + with, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID) + base := BasicAuditEvent(uid, formatTime(ev.MTime), MessageShareUpdated(uid, ev.ShareID.OpaqueId, ev.Updated), updateType(ev.Updated)) + return AuditEventShareUpdated{ + AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base), + ShareOwner: uid, + ShareWith: with, + ShareType: typ, + Permissions: ev.Permissions.Permissions.String(), + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + ExpirationDate: "", + SharePass: false, + ShareToken: "", + } +} + +func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { + switch { + case uid != nil && uid.OpaqueId != "": + return uid.OpaqueId, "user" + case gid != nil && gid.OpaqueId != "": + return gid.OpaqueId, "group" + } + + return "", "" +} + +func formatTime(t *types.Timestamp) string { + return time.Unix(int64(t.Seconds), int64(t.Nanos)).Format(time.RFC3339) +} + +func updateType(u string) string { + switch { + case u == "permissions": + return ActionSharePermissionUpdated + case u == "displayname": + return ActionShareDisplayNameUpdated + default: + return "" + } +} diff --git a/audit/pkg/types/events.go b/audit/pkg/types/events.go index 7a9825ef0..6f698e32d 100644 --- a/audit/pkg/types/events.go +++ b/audit/pkg/types/events.go @@ -8,5 +8,6 @@ import ( func RegisteredEvents() []events.Unmarshaller { return []events.Unmarshaller{ events.ShareCreated{}, + events.ShareUpdated{}, } } diff --git a/audit/pkg/types/types.go b/audit/pkg/types/types.go index cd07e994f..43c2715f4 100644 --- a/audit/pkg/types/types.go +++ b/audit/pkg/types/types.go @@ -38,3 +38,17 @@ type AuditEventShareCreated struct { ShareOwner string // The UID of the share owner. ShareToken string // For link shares the unique token, else null } + +// AuditEventShareUpdated is the event logged when a share is updated +type AuditEventShareUpdated struct { + AuditEventSharing + + ItemType string // file or folder + ExpirationDate string // The text expiration date in format 'yyyy-mm-dd' + SharePass bool // If the share is password protected. + Permissions string // The permissions string eg: "READ" + ShareType string // group user or link + ShareWith string // The UID or GID of the share recipient. (not available for public link) + ShareOwner string // The UID of the share owner. + ShareToken string // For link shares the unique token, else null +} diff --git a/go.mod b/go.mod index 829dead3e..123ee84b2 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/blevesearch/bleve/v2 v2.3.1 github.com/coreos/go-oidc/v3 v3.1.0 github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19 - github.com/cs3org/reva/v2 v2.0.0-20220304131900-b8be80d1ba81 + github.com/cs3org/reva/v2 v2.0.0 github.com/disintegration/imaging v1.6.2 github.com/glauth/glauth/v2 v2.0.0-20211021011345-ef3151c28733 github.com/go-chi/chi/v5 v5.0.7 @@ -276,3 +276,5 @@ require ( // we need to use a fork to make the windows build pass replace github.com/pkg/xattr => github.com/micbar/xattr v0.4.6-0.20220215112335-88e74d648fb7 + +replace github.com/cs3org/reva/v2 => github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77 diff --git a/go.sum b/go.sum index ac07e635c..c2ae4629c 100644 --- a/go.sum +++ b/go.sum @@ -342,8 +342,6 @@ github.com/crewjam/saml v0.4.5/go.mod h1:qCJQpUtZte9R1ZjUBcW8qtCNlinbO363ooNl02S github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19 h1:1jqPH58jCxvbaJ9WLIJ7W2/m622bWS6ChptzljSG6IQ= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.0.0-20220304131900-b8be80d1ba81 h1:g6c1HYGTSpDnf6uNPXYIOySVk0P545zWUPmdPWEcMps= -github.com/cs3org/reva/v2 v2.0.0-20220304131900-b8be80d1ba81/go.mod h1:XNtK1HEClNzmz5vyQa2DUw4KH3oqBjQoEsV1LhAGlV0= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -912,6 +910,8 @@ github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= +github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77 h1:8SY2Q0iV7yw+3gmr2o70OKHHcNZTpmfR07iz11Z/pN0= +github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77/go.mod h1:XNtK1HEClNzmz5vyQa2DUw4KH3oqBjQoEsV1LhAGlV0= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 7f0fff0806586d5eefa8e71a00c5fdc6e1c210a3 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 11 Mar 2022 12:46:24 +0100 Subject: [PATCH 02/10] link created/updated event Signed-off-by: jkoberg --- audit/pkg/service/service.go | 4 +++ audit/pkg/types/constants.go | 12 ++++++++ audit/pkg/types/conversion.go | 52 +++++++++++++++++++++++++++++++++++ audit/pkg/types/events.go | 2 ++ 4 files changed, 70 insertions(+) diff --git a/audit/pkg/service/service.go b/audit/pkg/service/service.go index 31bb76078..64321f025 100644 --- a/audit/pkg/service/service.go +++ b/audit/pkg/service/service.go @@ -45,8 +45,12 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger switch ev := i.(type) { case events.ShareCreated: auditEvent = types.ShareCreated(ev) + case events.LinkCreated: + auditEvent = types.LinkCreated(ev) case events.ShareUpdated: auditEvent = types.ShareUpdated(ev) + case events.LinkUpdated: + auditEvent = types.LinkUpdated(ev) default: log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev)) continue diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go index 33dd335f1..162a1e318 100644 --- a/audit/pkg/types/constants.go +++ b/audit/pkg/types/constants.go @@ -7,6 +7,8 @@ const ( ActionShareCreated = "share_created" ActionSharePermissionUpdated = "share_permission_updated" ActionShareDisplayNameUpdated = "share_name_updated" + ActionSharePasswordUpdated = "share_password_updated" + ActionShareExpirationUpdated = "share_expiration_updated" ) // MessageShareCreated returns the human readable string that describes the action @@ -14,7 +16,17 @@ func MessageShareCreated(sharer, item, grantee string) string { return fmt.Sprintf("user '%s' shared file '%s' with '%s'", sharer, item, grantee) } +// MessageLinkCreated returns the human readable string that describes the action +func MessageLinkCreated(sharer, item, shareid string) string { + return fmt.Sprintf("user '%s' created a public to file '%s' with id '%s'", sharer, item, shareid) +} + // MessageShareUpdated returns the human readable string that describes the action func MessageShareUpdated(sharer, shareID, fieldUpdated string) string { return fmt.Sprintf("user '%s' updated field '%s' of share '%s'", sharer, fieldUpdated, shareID) } + +// MessageLinkUpdated returns the human readable string that describes the action +func MessageLinkUpdated(sharer, shareid, fieldUpdated string) string { + return fmt.Sprintf("user '%s' modified field '%s' of public link '%s'", sharer, fieldUpdated, shareid) +} diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index e939310b7..89bde23a0 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -1,6 +1,7 @@ package types import ( + "fmt" "time" "github.com/cs3org/reva/v2/pkg/events" @@ -62,6 +63,26 @@ func ShareCreated(ev events.ShareCreated) AuditEventShareCreated { } } +// LinkCreated converts a ShareCreated Event to an AuditEventShareCreated +func LinkCreated(ev events.LinkCreated) AuditEventShareCreated { + uid := ev.Sharer.OpaqueId + with, typ := "", "link" + base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageLinkCreated(uid, ev.ItemID.OpaqueId, ev.ShareID.OpaqueId), ActionShareCreated) + return AuditEventShareCreated{ + AuditEventSharing: SharingAuditEvent("", ev.ItemID.OpaqueId, uid, base), + ShareOwner: uid, + ShareWith: with, + ShareType: typ, + ExpirationDate: formatTime(ev.Expiration), + SharePass: ev.PasswordProtected, + Permissions: ev.Permissions.String(), + ShareToken: ev.Token, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } +} + // ShareUpdated converts a ShareUpdated event to an AuditEventShareUpdated func ShareUpdated(ev events.ShareUpdated) AuditEventShareUpdated { uid := ev.Sharer.OpaqueId @@ -82,6 +103,25 @@ func ShareUpdated(ev events.ShareUpdated) AuditEventShareUpdated { } } +// LinkUpdated converts a LinkUpdated event to an AuditEventShareUpdated +func LinkUpdated(ev events.LinkUpdated) AuditEventShareUpdated { + uid := ev.Sharer.OpaqueId + with, typ := "", "link" + base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageLinkUpdated(uid, ev.ShareID.OpaqueId, ev.FieldUpdated), updateType(ev.FieldUpdated)) + return AuditEventShareUpdated{ + AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base), + ShareOwner: uid, + ShareWith: with, + ShareType: typ, + Permissions: ev.Permissions.Permissions.String(), + ExpirationDate: formatTime(ev.Expiration), + SharePass: ev.PasswordProtected, + ShareToken: ev.Token, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } +} func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { switch { case uid != nil && uid.OpaqueId != "": @@ -94,6 +134,9 @@ func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { } func formatTime(t *types.Timestamp) string { + if t == nil { + return "" + } return time.Unix(int64(t.Seconds), int64(t.Nanos)).Format(time.RFC3339) } @@ -103,7 +146,16 @@ func updateType(u string) string { return ActionSharePermissionUpdated case u == "displayname": return ActionShareDisplayNameUpdated + case u == "TYPE_PERMISSIONS": + return ActionSharePermissionUpdated + case u == "TYPE_DISPLAYNAME": + return ActionShareDisplayNameUpdated + case u == "TYPE_PASSWORD": + return ActionSharePasswordUpdated + case u == "TYPE_EXPIRATION": + return ActionShareExpirationUpdated default: + fmt.Println("Unknown update type", u) return "" } } diff --git a/audit/pkg/types/events.go b/audit/pkg/types/events.go index 6f698e32d..aa374ea4c 100644 --- a/audit/pkg/types/events.go +++ b/audit/pkg/types/events.go @@ -9,5 +9,7 @@ func RegisteredEvents() []events.Unmarshaller { return []events.Unmarshaller{ events.ShareCreated{}, events.ShareUpdated{}, + events.LinkCreated{}, + events.LinkUpdated{}, } } From 5f45e77967791cdfe137df8eab10bc8b79315490 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 11 Mar 2022 12:51:35 +0100 Subject: [PATCH 03/10] add changelog Signed-off-by: jkoberg --- changelog/unreleased/sharing-audit-events.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/sharing-audit-events.md diff --git a/changelog/unreleased/sharing-audit-events.md b/changelog/unreleased/sharing-audit-events.md new file mode 100644 index 000000000..5065e2d83 --- /dev/null +++ b/changelog/unreleased/sharing-audit-events.md @@ -0,0 +1,5 @@ +Enhancement: log sharing events in audit service + +Contains sharing related events. See full list in audit/pkg/types/events.go + +https://github.com/owncloud/ocis/pull/3301 From 0de5dac34c4c414158e43e8227d411bfa48848da Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 11 Mar 2022 13:49:34 +0100 Subject: [PATCH 04/10] share/link removed events Signed-off-by: jkoberg --- audit/pkg/service/service.go | 4 ++++ audit/pkg/types/constants.go | 13 ++++++++++- audit/pkg/types/conversion.go | 44 +++++++++++++++++++++++++++++++++++ audit/pkg/types/events.go | 2 ++ audit/pkg/types/types.go | 8 +++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/audit/pkg/service/service.go b/audit/pkg/service/service.go index 64321f025..e60d0649c 100644 --- a/audit/pkg/service/service.go +++ b/audit/pkg/service/service.go @@ -51,6 +51,10 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger auditEvent = types.ShareUpdated(ev) case events.LinkUpdated: auditEvent = types.LinkUpdated(ev) + case events.ShareRemoved: + auditEvent = types.ShareRemoved(ev) + case events.LinkRemoved: + auditEvent = types.LinkRemoved(ev) default: log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev)) continue diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go index 162a1e318..2d954bb0b 100644 --- a/audit/pkg/types/constants.go +++ b/audit/pkg/types/constants.go @@ -4,11 +4,12 @@ import "fmt" // short identifiers for audit actions const ( - ActionShareCreated = "share_created" + ActionShareCreated = "file_shared" ActionSharePermissionUpdated = "share_permission_updated" ActionShareDisplayNameUpdated = "share_name_updated" ActionSharePasswordUpdated = "share_password_updated" ActionShareExpirationUpdated = "share_expiration_updated" + ActionShareRemoved = "file_unshared" ) // MessageShareCreated returns the human readable string that describes the action @@ -30,3 +31,13 @@ func MessageShareUpdated(sharer, shareID, fieldUpdated string) string { func MessageLinkUpdated(sharer, shareid, fieldUpdated string) string { return fmt.Sprintf("user '%s' modified field '%s' of public link '%s'", sharer, fieldUpdated, shareid) } + +// MessageShareRemoved returns the human readable string that describes the action +func MessageShareRemoved(sharer, shareid, itemid string) string { + return fmt.Sprintf("share id:'%s' uid:'%s' item-id:'%s' was removed", shareid, sharer, itemid) +} + +// MessageLinkRemoved returns the human readable string that describes the action +func MessageLinkRemoved(shareid string) string { + return fmt.Sprintf("public link id:'%s' was removed", shareid) +} diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index 89bde23a0..49498ea40 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -122,6 +122,50 @@ func LinkUpdated(ev events.LinkUpdated) AuditEventShareUpdated { ItemType: "", } } + +// ShareRemoved converts a ShareRemoved event to an AuditEventShareRemoved +func ShareRemoved(ev events.ShareRemoved) AuditEventShareRemoved { + sid, uid, iid, with, typ := "", "", "", "", "" + if ev.ShareID != nil { + sid = ev.ShareID.GetOpaqueId() + } + + if ev.ShareKey != nil { + uid = ev.ShareKey.GetOwner().GetOpaqueId() + iid = ev.ShareKey.GetResourceId().GetOpaqueId() + with, typ = extractGrantee(ev.ShareKey.GetGrantee().GetUserId(), ev.ShareKey.GetGrantee().GetGroupId()) + } + base := BasicAuditEvent(uid, "", MessageShareRemoved(uid, sid, iid), ActionShareRemoved) + return AuditEventShareRemoved{ + AuditEventSharing: SharingAuditEvent(sid, iid, uid, base), + ShareWith: with, + ShareType: typ, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } +} + +// LinkRemoved converts a LinkRemoved event to an AuditEventShareRemoved +func LinkRemoved(ev events.LinkRemoved) AuditEventShareRemoved { + uid, sid, typ := "", "", "link" + if ev.ShareID != nil { + sid = ev.ShareID.GetOpaqueId() + } else { + sid = ev.ShareToken + } + + base := BasicAuditEvent(uid, "", MessageLinkRemoved(sid), ActionShareRemoved) + return AuditEventShareRemoved{ + AuditEventSharing: SharingAuditEvent(sid, "", uid, base), + ShareWith: "", + ShareType: typ, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } +} + func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { switch { case uid != nil && uid.OpaqueId != "": diff --git a/audit/pkg/types/events.go b/audit/pkg/types/events.go index aa374ea4c..b5e388135 100644 --- a/audit/pkg/types/events.go +++ b/audit/pkg/types/events.go @@ -11,5 +11,7 @@ func RegisteredEvents() []events.Unmarshaller { events.ShareUpdated{}, events.LinkCreated{}, events.LinkUpdated{}, + events.ShareRemoved{}, + events.LinkRemoved{}, } } diff --git a/audit/pkg/types/types.go b/audit/pkg/types/types.go index 43c2715f4..06ca53386 100644 --- a/audit/pkg/types/types.go +++ b/audit/pkg/types/types.go @@ -52,3 +52,11 @@ type AuditEventShareUpdated struct { ShareOwner string // The UID of the share owner. ShareToken string // For link shares the unique token, else null } + +// AuditEventShareRemoved is the event logged when a share is removed +type AuditEventShareRemoved struct { + AuditEventSharing + ItemType string // file or folder + ShareType string // group user or link + ShareWith string // The UID or GID of the share recipient. +} From ef642ac13584ee0fbfb805e791a83446963def38 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Sat, 12 Mar 2022 13:08:46 +0100 Subject: [PATCH 05/10] refactor unit tests Signed-off-by: jkoberg --- audit/pkg/service/service_test.go | 237 ++++++++++++++++++++++++++---- audit/pkg/types/constants.go | 2 +- 2 files changed, 210 insertions(+), 29 deletions(-) diff --git a/audit/pkg/service/service_test.go b/audit/pkg/service/service_test.go index 0724acc39..b58175af7 100644 --- a/audit/pkg/service/service_test.go +++ b/audit/pkg/service/service_test.go @@ -12,6 +12,8 @@ import ( group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" rtypes "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" ) @@ -22,37 +24,121 @@ var testCases = []struct { CheckAuditEvent func(*testing.T, []byte) }{ { - Alias: "ShareCreated", + Alias: "ShareCreated - user", SystemEvent: events.ShareCreated{ - Sharer: &user.UserId{ - OpaqueId: "sharing-userid", - Idp: "idp", - }, - GranteeUserID: &user.UserId{ - OpaqueId: "beshared-userid", - Idp: "idp", - }, - GranteeGroupID: &group.GroupId{}, - Sharee: &provider.Grantee{}, - ItemID: &provider.ResourceId{ - StorageId: "storage-1", - OpaqueId: "itemid-1", - }, - CTime: &rtypes.Timestamp{ - Seconds: 0, - Nanos: 0, - }, + Sharer: userID("sharing-userid"), + GranteeUserID: userID("beshared-userid"), + GranteeGroupID: nil, + ItemID: resourceID("storage-1", "itemid-1"), + CTime: timestamp(0), }, CheckAuditEvent: func(t *testing.T, b []byte) { ev := types.AuditEventShareCreated{} - err := json.Unmarshal(b, &ev) - require.NoError(t, err) + require.NoError(t, json.Unmarshal(b, &ev)) - require.Equal(t, ev.User, "sharing-userid") - require.Equal(t, ev.ShareWith, "beshared-userid") - require.Equal(t, ev.FileID, "itemid-1") - require.Equal(t, ev.Time, "1970-01-01T01:00:00+01:00") + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "1970-01-01T01:00:00+01:00", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-userid'", "file_shared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "") + // AuditEventShareCreated fields + require.Equal(t, "", ev.ItemType) + require.Equal(t, "", ev.ExpirationDate) + require.Equal(t, false, ev.SharePass) + //require.Equal(t, "stat:true ", ev.Permissions) // TODO: BUG! Should work + require.Equal(t, "user", ev.ShareType) + require.Equal(t, "beshared-userid", ev.ShareWith) + require.Equal(t, "sharing-userid", ev.ShareOwner) + require.Equal(t, "", ev.ShareToken) + }, + }, { + Alias: "ShareCreated - group", + SystemEvent: events.ShareCreated{ + Sharer: userID("sharing-userid"), + GranteeUserID: nil, + GranteeGroupID: groupID("beshared-groupid"), + ItemID: resourceID("storage-1", "itemid-1"), + CTime: timestamp(10e8), + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareCreated{} + require.NoError(t, json.Unmarshal(b, &ev)) + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-groupid'", "file_shared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "") + // AuditEventShareCreated fields + require.Equal(t, "", ev.ItemType) + require.Equal(t, "", ev.ExpirationDate) + require.Equal(t, false, ev.SharePass) + //require.Equal(t, "stat:true ", ev.Permissions) // TODO: BUG! Should work + require.Equal(t, "group", ev.ShareType) + require.Equal(t, "beshared-groupid", ev.ShareWith) + require.Equal(t, "sharing-userid", ev.ShareOwner) + require.Equal(t, "", ev.ShareToken) + + }, + }, { + Alias: "ShareUpdated", + SystemEvent: events.ShareUpdated{ + ShareID: shareID("shareid"), + Sharer: userID("sharing-userid"), + GranteeUserID: nil, + GranteeGroupID: groupID("beshared-groupid"), + ItemID: resourceID("storage-1", "itemid-1"), + Permissions: sharePermissions("stat", "get_quota"), + MTime: timestamp(10e8), + Updated: "permissions", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareUpdated{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' updated field 'permissions' of share 'shareid'", "share_permission_updated") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "", ev.ExpirationDate) // no expiration for shares + require.Equal(t, false, ev.SharePass) + require.Equal(t, "get_quota:true stat:true ", ev.Permissions) + require.Equal(t, "group", ev.ShareType) + require.Equal(t, "beshared-groupid", ev.ShareWith) + require.Equal(t, "sharing-userid", ev.ShareOwner) + require.Equal(t, "", ev.ShareToken) // token not filled for shares + }, + }, { + Alias: "LinkUpdated - permissions", + SystemEvent: events.LinkUpdated{ + ShareID: linkID("shareid"), + Sharer: userID("sharing-userid"), + ItemID: resourceID("storage-1", "itemid-1"), + Permissions: linkPermissions("stat"), + CTime: timestamp(10e8), + DisplayName: "link", + Expiration: timestamp(10e8 + 10e5), + PasswordProtected: true, + Token: "token-123", + FieldUpdated: "permissions", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareUpdated{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' updated field 'permissions' of public link 'shareid'", "share_permission_updated") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "2001-09-20T17:33:20+02:00", ev.ExpirationDate) + require.Equal(t, true, ev.SharePass) + require.Equal(t, "stat:true ", ev.Permissions) + require.Equal(t, "link", ev.ShareType) + require.Equal(t, "", ev.ShareWith) // not filled on links + require.Equal(t, "sharing-userid", ev.ShareOwner) + require.Equal(t, "token-123", ev.ShareToken) }, }, } @@ -73,8 +159,103 @@ func TestAuditLogging(t *testing.T) { outch <- b }) - for _, tc := range testCases { - inch <- tc.SystemEvent - tc.CheckAuditEvent(t, <-outch) + for i := range testCases { + tc := testCases[i] + t.Run(tc.Alias, func(t *testing.T) { + inch <- tc.SystemEvent + tc.CheckAuditEvent(t, <-outch) + }) } } + +func checkBaseAuditEvent(t *testing.T, ev types.AuditEvent, user string, time string, message string, action string) { + require.Equal(t, "", ev.RemoteAddr) // not implemented atm + require.Equal(t, user, ev.User) + require.Equal(t, "", ev.URL) // not implemented atm + require.Equal(t, "", ev.Method) // not implemented atm + require.Equal(t, "", ev.UserAgent) // not implemented atm + require.Equal(t, time, ev.Time) + require.Equal(t, "admin_audit", ev.App) + require.Equal(t, message, ev.Message) + require.Equal(t, action, ev.Action) + require.Equal(t, false, ev.CLI) // not implemented atm + require.Equal(t, 1, ev.Level) +} + +func checkSharingAuditEvent(t *testing.T, ev types.AuditEventSharing, itemID string, owner string, shareID string) { + require.Equal(t, itemID, ev.FileID) + require.Equal(t, owner, ev.Owner) + require.Equal(t, "", ev.Path) // not implemented atm + require.Equal(t, shareID, ev.ShareID) +} + +func shareID(id string) *collaboration.ShareId { + return &collaboration.ShareId{ + OpaqueId: id, + } +} + +func linkID(id string) *link.PublicShareId { + return &link.PublicShareId{ + OpaqueId: id, + } +} + +func userID(id string) *user.UserId { + return &user.UserId{ + OpaqueId: id, + Idp: "idp", + } +} + +func groupID(id string) *group.GroupId { + return &group.GroupId{ + OpaqueId: id, + Idp: "idp", + } +} + +func resourceID(sid, oid string) *provider.ResourceId { + return &provider.ResourceId{ + StorageId: sid, + OpaqueId: oid, + } +} + +func timestamp(seconds uint64) *rtypes.Timestamp { + return &rtypes.Timestamp{ + Seconds: seconds, + Nanos: 0, + } +} + +func sharePermissions(perms ...string) *collaboration.SharePermissions { + return &collaboration.SharePermissions{ + Permissions: permissions(perms...), + } +} + +func linkPermissions(perms ...string) *link.PublicSharePermissions { + return &link.PublicSharePermissions{ + Permissions: permissions(perms...), + } +} +func permissions(permissions ...string) *provider.ResourcePermissions { + perms := &provider.ResourcePermissions{} + + for _, p := range permissions { + switch p { + case "stat": + perms.Stat = true + case "get_path": + perms.GetPath = true + case "list_container": + perms.ListContainer = true + case "get_quota": + perms.GetQuota = true + + } + } + + return perms +} diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go index 2d954bb0b..00084862e 100644 --- a/audit/pkg/types/constants.go +++ b/audit/pkg/types/constants.go @@ -29,7 +29,7 @@ func MessageShareUpdated(sharer, shareID, fieldUpdated string) string { // MessageLinkUpdated returns the human readable string that describes the action func MessageLinkUpdated(sharer, shareid, fieldUpdated string) string { - return fmt.Sprintf("user '%s' modified field '%s' of public link '%s'", sharer, fieldUpdated, shareid) + return fmt.Sprintf("user '%s' updated field '%s' of public link '%s'", sharer, fieldUpdated, shareid) } // MessageShareRemoved returns the human readable string that describes the action From 2fa08495f76cb06953343e22f5b281aa6ee8ca40 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Sat, 12 Mar 2022 13:43:51 +0100 Subject: [PATCH 06/10] more unit tests and events Signed-off-by: jkoberg --- audit/pkg/service/service.go | 6 +++ audit/pkg/service/service_test.go | 82 +++++++++++++++++++++++++++++++ audit/pkg/types/conversion.go | 15 ++++++ audit/pkg/types/events.go | 3 ++ audit/pkg/types/types.go | 20 ++++++++ 5 files changed, 126 insertions(+) diff --git a/audit/pkg/service/service.go b/audit/pkg/service/service.go index e60d0649c..905e251ee 100644 --- a/audit/pkg/service/service.go +++ b/audit/pkg/service/service.go @@ -55,6 +55,12 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger auditEvent = types.ShareRemoved(ev) case events.LinkRemoved: auditEvent = types.LinkRemoved(ev) + case events.ReceivedShareUpdated: + auditEvent = types.ReceivedShareUpdated(ev) + case events.LinkAccessed: + auditEvent = types.LinkAccessed(ev) + case events.LinkAccessFailed: + auditEvent = types.LinkAccessFailed(ev) default: log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev)) continue diff --git a/audit/pkg/service/service_test.go b/audit/pkg/service/service_test.go index b58175af7..4ec5a439d 100644 --- a/audit/pkg/service/service_test.go +++ b/audit/pkg/service/service_test.go @@ -140,6 +140,88 @@ var testCases = []struct { require.Equal(t, "sharing-userid", ev.ShareOwner) require.Equal(t, "token-123", ev.ShareToken) }, + }, { + Alias: "ShareRemoved", + SystemEvent: events.ShareRemoved{ + ShareID: shareID("shareid"), + ShareKey: nil, + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareRemoved{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "", "", "share id:'shareid' uid:'' item-id:'' was removed", "file_unshared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "", ev.ShareType) + require.Equal(t, "", ev.ShareWith) // not filled on links + }, + }, { + Alias: "LinkRemoved - id", + SystemEvent: events.LinkRemoved{ + ShareID: linkID("shareid"), + ShareToken: "", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareRemoved{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "", "", "public link id:'shareid' was removed", "file_unshared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "link", ev.ShareType) + require.Equal(t, "", ev.ShareWith) // not filled on links + }, + }, { + Alias: "LinkRemoved - token", + SystemEvent: events.LinkRemoved{ + ShareID: nil, + ShareToken: "token-123", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventShareRemoved{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "", "", "public link id:'token-123' was removed", "file_unshared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "token-123") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "link", ev.ShareType) + require.Equal(t, "", ev.ShareWith) // not filled on links + }, + }, { + Alias: "Share accepted", + SystemEvent: events.ReceivedShareUpdated{ + ShareID: shareID("shareid"), + ItemID: resourceID("storageid-1", "itemid-1"), + Permissions: sharePermissions("get_quota"), + GranteeUserID: userID("beshared-userid"), + GranteeGroupID: nil, + Sharer: userID("sharing-userid"), + MTime: timestamp(10e8), + State: "SHARE_STATE_ACCEPTED", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventReceivedShareUpdated{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "", "", "public link id:'token-123' was removed", "file_unshared") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "token-123") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "link", ev.ShareType) + require.Equal(t, "", ev.ShareWith) // not filled on links + }, }, } diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index 49498ea40..79d1b3fbf 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -166,6 +166,21 @@ func LinkRemoved(ev events.LinkRemoved) AuditEventShareRemoved { } } +// ReceivedShareUpdated converts a ReceivedShareUpdated event to an AuditEventReceivedShareUpdated +func ReceivedShareUpdated(ev events.ReceivedShareUpdated) AuditEventReceivedShareUpdated { + return AuditEventReceivedShareUpdated{} +} + +// LinkAccessed converts a LinkAccessed event to an AuditEventLinkAccessed +func LinkAccessed(ev events.LinkAccessed) AuditEventLinkAccessed { + return AuditEventLinkAccessed{} +} + +// LinkAccessFailed converts a LinkAccessFailed event to an AuditEventLinkAccessed +func LinkAccessFailed(ev events.LinkAccessFailed) AuditEventLinkAccessed { + return AuditEventLinkAccessed{} +} + func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { switch { case uid != nil && uid.OpaqueId != "": diff --git a/audit/pkg/types/events.go b/audit/pkg/types/events.go index b5e388135..801dd4179 100644 --- a/audit/pkg/types/events.go +++ b/audit/pkg/types/events.go @@ -13,5 +13,8 @@ func RegisteredEvents() []events.Unmarshaller { events.LinkUpdated{}, events.ShareRemoved{}, events.LinkRemoved{}, + events.ReceivedShareUpdated{}, + events.LinkAccessed{}, + events.LinkAccessFailed{}, } } diff --git a/audit/pkg/types/types.go b/audit/pkg/types/types.go index 06ca53386..abe4ac671 100644 --- a/audit/pkg/types/types.go +++ b/audit/pkg/types/types.go @@ -60,3 +60,23 @@ type AuditEventShareRemoved struct { ShareType string // group user or link ShareWith string // The UID or GID of the share recipient. } + +// AuditEventReceivedShareUpdated is the event logged when a share is accepted or declined +type AuditEventReceivedShareUpdated struct { + AuditEventSharing + ItemType string // file or folder + ShareType string // group user or link + ShareWith string // The UID or GID of the share recipient. + Path string // The path of the shared item. + Owner string // The UID of the owner of the shared item. + FileID string // The file identifier for the item shared. + ShareID string // The sharing identifier. (not available for public_link_accessed) +} + +// AuditEventLinkAccessed is the event logged when a link is accessed +type AuditEventLinkAccessed struct { + AuditEventSharing + ShareToken string // The share token. + Success bool // If the request was successful. + ItemType string // file or folder +} From f01263859b594ccf4fca188977f7b80d63c3786b Mon Sep 17 00:00:00 2001 From: jkoberg Date: Sat, 12 Mar 2022 14:19:41 +0100 Subject: [PATCH 07/10] share accepted/declined events Signed-off-by: jkoberg --- audit/pkg/service/service_test.go | 35 ++++++++++++++++++++++++++----- audit/pkg/types/constants.go | 18 ++++++++++++++++ audit/pkg/types/conversion.go | 24 ++++++++++++++++++++- audit/pkg/types/types.go | 4 ---- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/audit/pkg/service/service_test.go b/audit/pkg/service/service_test.go index 4ec5a439d..bb60d064a 100644 --- a/audit/pkg/service/service_test.go +++ b/audit/pkg/service/service_test.go @@ -214,13 +214,38 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "", "", "public link id:'token-123' was removed", "file_unshared") + checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T03:46:40+02:00", "user 'beshared-userid' accepted share 'shareid' from user 'sharing-userid'", "share_accepted") // AuditEventSharing fields - checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "token-123") + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields - require.Equal(t, "", ev.ItemType) // not implemented atm - require.Equal(t, "link", ev.ShareType) - require.Equal(t, "", ev.ShareWith) // not filled on links + require.Equal(t, "", ev.ItemType) + require.Equal(t, "user", ev.ShareType) + require.Equal(t, "beshared-userid", ev.ShareWith) + }, + }, { + Alias: "Share declined", + SystemEvent: events.ReceivedShareUpdated{ + ShareID: shareID("shareid"), + ItemID: resourceID("storageid-1", "itemid-1"), + Permissions: sharePermissions("get_quota"), + GranteeUserID: userID("beshared-userid"), + GranteeGroupID: nil, + Sharer: userID("sharing-userid"), + MTime: timestamp(10e8), + State: "SHARE_STATE_DECLINED", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventReceivedShareUpdated{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T03:46:40+02:00", "user 'beshared-userid' declined share 'shareid' from user 'sharing-userid'", "share_declined") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) + require.Equal(t, "user", ev.ShareType) + require.Equal(t, "beshared-userid", ev.ShareWith) }, }, } diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go index 00084862e..a8372249e 100644 --- a/audit/pkg/types/constants.go +++ b/audit/pkg/types/constants.go @@ -10,6 +10,9 @@ const ( ActionSharePasswordUpdated = "share_password_updated" ActionShareExpirationUpdated = "share_expiration_updated" ActionShareRemoved = "file_unshared" + ActionShareAccepted = "share_accepted" + ActionShareDeclined = "share_declined" + ActionLinkAccessed = "public_link_accessed" ) // MessageShareCreated returns the human readable string that describes the action @@ -41,3 +44,18 @@ func MessageShareRemoved(sharer, shareid, itemid string) string { func MessageLinkRemoved(shareid string) string { return fmt.Sprintf("public link id:'%s' was removed", shareid) } + +// MessageShareAccepted returns the human readable string that describes the action +func MessageShareAccepted(userid, shareid, sharerid string) string { + return fmt.Sprintf("user '%s' accepted share '%s' from user '%s'", userid, shareid, sharerid) +} + +// MessageShareDeclined returns the human readable string that describes the action +func MessageShareDeclined(userid, shareid, sharerid string) string { + return fmt.Sprintf("user '%s' declined share '%s' from user '%s'", userid, shareid, sharerid) +} + +// MessageLinkAccessed returns the human readable string that describes the action +func MessageLinkAccessed(linkid string, success bool) string { + return fmt.Sprintf("link '%s' was accessed. Success: %b", linkid, success) +} diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index 79d1b3fbf..a0c55384e 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -168,7 +168,29 @@ func LinkRemoved(ev events.LinkRemoved) AuditEventShareRemoved { // ReceivedShareUpdated converts a ReceivedShareUpdated event to an AuditEventReceivedShareUpdated func ReceivedShareUpdated(ev events.ReceivedShareUpdated) AuditEventReceivedShareUpdated { - return AuditEventReceivedShareUpdated{} + uid := ev.Sharer.GetOpaqueId() + sid := ev.ShareID.GetOpaqueId() + with, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID) + itemID := ev.ItemID.GetOpaqueId() + + msg, utype := "", "" + switch ev.State { + case "SHARE_STATE_ACCEPTED": + msg = MessageShareAccepted(with, sid, uid) + utype = ActionShareAccepted + case "SHARE_STATE_DECLINED": + msg = MessageShareDeclined(with, sid, uid) + utype = ActionShareDeclined + } + base := BasicAuditEvent(with, formatTime(ev.MTime), msg, utype) + return AuditEventReceivedShareUpdated{ + AuditEventSharing: SharingAuditEvent(sid, itemID, uid, base), + ShareType: typ, + ShareWith: with, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } } // LinkAccessed converts a LinkAccessed event to an AuditEventLinkAccessed diff --git a/audit/pkg/types/types.go b/audit/pkg/types/types.go index abe4ac671..17e2d0a3e 100644 --- a/audit/pkg/types/types.go +++ b/audit/pkg/types/types.go @@ -67,10 +67,6 @@ type AuditEventReceivedShareUpdated struct { ItemType string // file or folder ShareType string // group user or link ShareWith string // The UID or GID of the share recipient. - Path string // The path of the shared item. - Owner string // The UID of the owner of the shared item. - FileID string // The file identifier for the item shared. - ShareID string // The sharing identifier. (not available for public_link_accessed) } // AuditEventLinkAccessed is the event logged when a link is accessed From 22bb75a5e4570e2b133557988de12a04cc434deb Mon Sep 17 00:00:00 2001 From: jkoberg Date: Sat, 12 Mar 2022 14:32:23 +0100 Subject: [PATCH 08/10] link access events Signed-off-by: jkoberg --- audit/pkg/service/service_test.go | 47 +++++++++++++++++++++++++++++++ audit/pkg/types/constants.go | 2 +- audit/pkg/types/conversion.go | 21 ++++++++++++-- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/audit/pkg/service/service_test.go b/audit/pkg/service/service_test.go index bb60d064a..a03a0c7c5 100644 --- a/audit/pkg/service/service_test.go +++ b/audit/pkg/service/service_test.go @@ -247,6 +247,53 @@ var testCases = []struct { require.Equal(t, "user", ev.ShareType) require.Equal(t, "beshared-userid", ev.ShareWith) }, + }, { + Alias: "Link accessed - success", + SystemEvent: events.LinkAccessed{ + ShareID: linkID("shareid"), + Sharer: userID("sharing-userid"), + ItemID: resourceID("storage-1", "itemid-1"), + Permissions: linkPermissions("stat"), + DisplayName: "link", + Expiration: timestamp(10e8 + 10e5), + PasswordProtected: true, + CTime: timestamp(10e8), + Token: "token-123", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventLinkAccessed{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "link 'shareid' was accessed. Success: true", "public_link_accessed") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "token-123", ev.ShareToken) + require.Equal(t, true, ev.Success) + }, + }, { + Alias: "Link accessed - failure", + SystemEvent: events.LinkAccessFailed{ + ShareID: linkID("shareid"), + Token: "token-123", + Status: 8, + Message: "access denied", + }, + CheckAuditEvent: func(t *testing.T, b []byte) { + ev := types.AuditEventLinkAccessed{} + require.NoError(t, json.Unmarshal(b, &ev)) + + // AuditEvent fields + checkBaseAuditEvent(t, ev.AuditEvent, "", "", "link 'shareid' was accessed. Success: false", "public_link_accessed") + // AuditEventSharing fields + checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "shareid") + // AuditEventShareUpdated fields + require.Equal(t, "", ev.ItemType) // not implemented atm + require.Equal(t, "token-123", ev.ShareToken) + require.Equal(t, false, ev.Success) + }, }, } diff --git a/audit/pkg/types/constants.go b/audit/pkg/types/constants.go index a8372249e..663996241 100644 --- a/audit/pkg/types/constants.go +++ b/audit/pkg/types/constants.go @@ -57,5 +57,5 @@ func MessageShareDeclined(userid, shareid, sharerid string) string { // MessageLinkAccessed returns the human readable string that describes the action func MessageLinkAccessed(linkid string, success bool) string { - return fmt.Sprintf("link '%s' was accessed. Success: %b", linkid, success) + return fmt.Sprintf("link '%s' was accessed. Success: %v", linkid, success) } diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index a0c55384e..47960787b 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -195,12 +195,29 @@ func ReceivedShareUpdated(ev events.ReceivedShareUpdated) AuditEventReceivedShar // LinkAccessed converts a LinkAccessed event to an AuditEventLinkAccessed func LinkAccessed(ev events.LinkAccessed) AuditEventLinkAccessed { - return AuditEventLinkAccessed{} + uid := ev.Sharer.OpaqueId + base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageLinkAccessed(ev.ShareID.GetOpaqueId(), true), ActionLinkAccessed) + return AuditEventLinkAccessed{ + AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base), + ShareToken: ev.Token, + Success: true, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } } // LinkAccessFailed converts a LinkAccessFailed event to an AuditEventLinkAccessed func LinkAccessFailed(ev events.LinkAccessFailed) AuditEventLinkAccessed { - return AuditEventLinkAccessed{} + base := BasicAuditEvent("", "", MessageLinkAccessed(ev.ShareID.GetOpaqueId(), false), ActionLinkAccessed) + return AuditEventLinkAccessed{ + AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), "", "", base), + ShareToken: ev.Token, + Success: false, + + // NOTE: those values are not in the event and can therefore not be filled at the moment + ItemType: "", + } } func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) { From 50d3ef44a4a819c20851561c4dd7f54376ce8c51 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 14 Mar 2022 10:10:11 +0100 Subject: [PATCH 09/10] bump reva & run unit tests Signed-off-by: jkoberg --- .drone.star | 1 + Makefile | 1 + audit/Makefile | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +--- go.sum | 4 ++-- 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 audit/Makefile diff --git a/.drone.star b/.drone.star index a501e4679..0bddbb7b9 100644 --- a/.drone.star +++ b/.drone.star @@ -16,6 +16,7 @@ config = { "modules": [ # if you add a module here please also add it to the root level Makefile "accounts", + "audit", "glauth", "graph-explorer", "graph", diff --git a/Makefile b/Makefile index de9c6924b..2bffcac21 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ L10N_MODULES := $(shell find . -path '*.tx*' -name 'config' | sed 's|/[^/]*$$||' # if you add a module here please also add it to the .drone.star file OCIS_MODULES = \ accounts \ + audit \ glauth \ graph \ graph-explorer \ diff --git a/audit/Makefile b/audit/Makefile new file mode 100644 index 000000000..b2661463c --- /dev/null +++ b/audit/Makefile @@ -0,0 +1,55 @@ +SHELL := bash +NAME := audit + +include ../.make/recursion.mk + +.PHONY: test-acceptance-webui +test-acceptance-webui: + ./ui/tests/run-acceptance-test.sh $(FEATURE_PATH) + + +############ tooling ############ +ifneq (, $(shell which go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI +include ../.bingo/Variables.mk +endif + +############ go tooling ############ +include ../.make/go.mk + +############ release ############ +include ../.make/release.mk + +############ docs generate ############ +include ../.make/docs.mk + +############ l10n ############ +include ../.make/l10n.mk + +.PHONY: docs-generate +docs-generate: config-docs-generate \ + grpc-docs-generate + +############ generate ############ +include ../.make/generate.mk + +.PHONY: ci-go-generate +ci-go-generate: protobuf # CI runs ci-node-generate automatically before this target + +.PHONY: ci-node-generate +ci-node-generate: yarn-build + +.PHONY: yarn-build +yarn-build: node_modules + yarn lint + yarn test + yarn build + +.PHONY: node_modules +node_modules: + yarn install --immutable + +############ protobuf ############ +include ../.make/protobuf.mk + +.PHONY: protobuf +protobuf: buf-generate diff --git a/go.mod b/go.mod index 123ee84b2..c07f3109e 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/blevesearch/bleve/v2 v2.3.1 github.com/coreos/go-oidc/v3 v3.1.0 github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19 - github.com/cs3org/reva/v2 v2.0.0 + github.com/cs3org/reva/v2 v2.0.0-20220314085001-8e5b22a20a3f github.com/disintegration/imaging v1.6.2 github.com/glauth/glauth/v2 v2.0.0-20211021011345-ef3151c28733 github.com/go-chi/chi/v5 v5.0.7 @@ -276,5 +276,3 @@ require ( // we need to use a fork to make the windows build pass replace github.com/pkg/xattr => github.com/micbar/xattr v0.4.6-0.20220215112335-88e74d648fb7 - -replace github.com/cs3org/reva/v2 => github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77 diff --git a/go.sum b/go.sum index c2ae4629c..a4f60af13 100644 --- a/go.sum +++ b/go.sum @@ -342,6 +342,8 @@ github.com/crewjam/saml v0.4.5/go.mod h1:qCJQpUtZte9R1ZjUBcW8qtCNlinbO363ooNl02S github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19 h1:1jqPH58jCxvbaJ9WLIJ7W2/m622bWS6ChptzljSG6IQ= github.com/cs3org/go-cs3apis v0.0.0-20220126114148-64c025ccdd19/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/reva/v2 v2.0.0-20220314085001-8e5b22a20a3f h1:tv7v6OjbFoDFNB2ikGC+LLaWEOIAJnrZjyO5LRTDL0g= +github.com/cs3org/reva/v2 v2.0.0-20220314085001-8e5b22a20a3f/go.mod h1:XNtK1HEClNzmz5vyQa2DUw4KH3oqBjQoEsV1LhAGlV0= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -910,8 +912,6 @@ github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= -github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77 h1:8SY2Q0iV7yw+3gmr2o70OKHHcNZTpmfR07iz11Z/pN0= -github.com/kobergj/reva/v2 v2.0.0-20220310160623-7d38443c6b77/go.mod h1:XNtK1HEClNzmz5vyQa2DUw4KH3oqBjQoEsV1LhAGlV0= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 5ea1cec83adfe78f7ae0f637404d91bea54c93ef Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 14 Mar 2022 11:03:30 +0100 Subject: [PATCH 10/10] use UTC timezone to avoid confusion Signed-off-by: jkoberg --- audit/pkg/service/service_test.go | 16 ++++++++-------- audit/pkg/types/conversion.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/audit/pkg/service/service_test.go b/audit/pkg/service/service_test.go index a03a0c7c5..8bdcac5cd 100644 --- a/audit/pkg/service/service_test.go +++ b/audit/pkg/service/service_test.go @@ -37,7 +37,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "1970-01-01T01:00:00+01:00", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-userid'", "file_shared") + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "1970-01-01T00:00:00Z", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-userid'", "file_shared") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "") // AuditEventShareCreated fields @@ -64,7 +64,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-groupid'", "file_shared") + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-groupid'", "file_shared") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "") // AuditEventShareCreated fields @@ -95,7 +95,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' updated field 'permissions' of share 'shareid'", "share_permission_updated") + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' updated field 'permissions' of share 'shareid'", "share_permission_updated") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields @@ -127,12 +127,12 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "user 'sharing-userid' updated field 'permissions' of public link 'shareid'", "share_permission_updated") + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' updated field 'permissions' of public link 'shareid'", "share_permission_updated") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields require.Equal(t, "", ev.ItemType) // not implemented atm - require.Equal(t, "2001-09-20T17:33:20+02:00", ev.ExpirationDate) + require.Equal(t, "2001-09-20T15:33:20Z", ev.ExpirationDate) require.Equal(t, true, ev.SharePass) require.Equal(t, "stat:true ", ev.Permissions) require.Equal(t, "link", ev.ShareType) @@ -214,7 +214,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T03:46:40+02:00", "user 'beshared-userid' accepted share 'shareid' from user 'sharing-userid'", "share_accepted") + checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T01:46:40Z", "user 'beshared-userid' accepted share 'shareid' from user 'sharing-userid'", "share_accepted") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields @@ -239,7 +239,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T03:46:40+02:00", "user 'beshared-userid' declined share 'shareid' from user 'sharing-userid'", "share_declined") + checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T01:46:40Z", "user 'beshared-userid' declined share 'shareid' from user 'sharing-userid'", "share_declined") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields @@ -265,7 +265,7 @@ var testCases = []struct { require.NoError(t, json.Unmarshal(b, &ev)) // AuditEvent fields - checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T03:46:40+02:00", "link 'shareid' was accessed. Success: true", "public_link_accessed") + checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "link 'shareid' was accessed. Success: true", "public_link_accessed") // AuditEventSharing fields checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid") // AuditEventShareUpdated fields diff --git a/audit/pkg/types/conversion.go b/audit/pkg/types/conversion.go index 47960787b..c90ab0eec 100644 --- a/audit/pkg/types/conversion.go +++ b/audit/pkg/types/conversion.go @@ -235,7 +235,7 @@ func formatTime(t *types.Timestamp) string { if t == nil { return "" } - return time.Unix(int64(t.Seconds), int64(t.Nanos)).Format(time.RFC3339) + return time.Unix(int64(t.Seconds), int64(t.Nanos)).UTC().Format(time.RFC3339) } func updateType(u string) string {