Merge pull request #3301 from kobergj/ShareEventsForAuditLogging

Share events for audit logging
This commit is contained in:
Willy Kloucek
2022-03-14 13:09:56 +01:00
committed by GitHub
12 changed files with 771 additions and 56 deletions

View File

@@ -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",

View File

@@ -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 \

55
audit/Makefile Normal file
View File

@@ -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

View File

@@ -45,6 +45,22 @@ 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)
case events.ShareRemoved:
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
@@ -95,5 +111,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
}
}
}

View File

@@ -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,275 @@ 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-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
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-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
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-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
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-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-20T15:33:20Z", 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)
},
}, {
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, "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
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-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
require.Equal(t, "", ev.ItemType)
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-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
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)
},
},
}
@@ -73,8 +313,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
}

View File

@@ -0,0 +1,61 @@
package types
import "fmt"
// short identifiers for audit actions
const (
ActionShareCreated = "file_shared"
ActionSharePermissionUpdated = "share_permission_updated"
ActionShareDisplayNameUpdated = "share_name_updated"
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
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' updated 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)
}
// 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: %v", linkid, success)
}

View File

@@ -5,16 +5,10 @@ import (
"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 +31,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 +62,198 @@ func ShareCreated(ev events.ShareCreated) AuditEventShareCreated {
ShareToken: "",
}
}
// 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
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: "",
}
}
// 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: "",
}
}
// 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: "",
}
}
// ReceivedShareUpdated converts a ReceivedShareUpdated event to an AuditEventReceivedShareUpdated
func ReceivedShareUpdated(ev events.ReceivedShareUpdated) 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
func LinkAccessed(ev events.LinkAccessed) 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 {
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) {
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 {
if t == nil {
return ""
}
return time.Unix(int64(t.Seconds), int64(t.Nanos)).UTC().Format(time.RFC3339)
}
func updateType(u string) string {
switch {
case u == "permissions":
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 ""
}
}

View File

@@ -8,5 +8,13 @@ import (
func RegisteredEvents() []events.Unmarshaller {
return []events.Unmarshaller{
events.ShareCreated{},
events.ShareUpdated{},
events.LinkCreated{},
events.LinkUpdated{},
events.ShareRemoved{},
events.LinkRemoved{},
events.ReceivedShareUpdated{},
events.LinkAccessed{},
events.LinkAccessFailed{},
}
}

View File

@@ -38,3 +38,41 @@ 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
}
// 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.
}
// 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.
}
// 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
}

View File

@@ -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

2
go.mod
View File

@@ -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-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

4
go.sum
View File

@@ -342,8 +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-20220304131900-b8be80d1ba81 h1:g6c1HYGTSpDnf6uNPXYIOySVk0P545zWUPmdPWEcMps=
github.com/cs3org/reva/v2 v2.0.0-20220304131900-b8be80d1ba81/go.mod h1:XNtK1HEClNzmz5vyQa2DUw4KH3oqBjQoEsV1LhAGlV0=
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=