Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File events audit logging #3332

Merged
merged 10 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions audit/pkg/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger
auditEvent = types.LinkAccessed(ev)
case events.LinkAccessFailed:
auditEvent = types.LinkAccessFailed(ev)
case events.FileUploaded:
auditEvent = types.FileUploaded(ev)
case events.FileDownloaded:
auditEvent = types.FileDownloaded(ev)
case events.ItemMoved:
auditEvent = types.ItemMoved(ev)
case events.ItemTrashed:
auditEvent = types.ItemTrashed(ev)
case events.ItemPurged:
auditEvent = types.ItemPurged(ev)
case events.ItemRestored:
auditEvent = types.ItemRestored(ev)
case events.FileVersionRestored:
auditEvent = types.FileVersionRestored(ev)
default:
log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev))
continue
Expand Down
132 changes: 132 additions & 0 deletions audit/pkg/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,124 @@ var testCases = []struct {
require.Equal(t, "token-123", ev.ShareToken)
require.Equal(t, false, ev.Success)
},
}, {
Alias: "File created",
SystemEvent: events.FileUploaded{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileCreated{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was created", "file_create")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
},
}, {
Alias: "File read",
SystemEvent: events.FileDownloaded{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileRead{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was read", "file_read")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
},
}, {
Alias: "File trashed",
SystemEvent: events.ItemTrashed{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileDeleted{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was trashed", "file_delete")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
},
}, {
Alias: "File renamed",
SystemEvent: events.ItemMoved{
FileID: reference("sto-123", "iid-123", "./item"),
OldReference: reference("sto-123", "iid-123", "./anotheritem"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileRenamed{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was moved from './anotheritem' to './item'", "file_rename")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
// AuditEventFileRenamed fields
require.Equal(t, "./anotheritem", ev.OldPath)

},
}, {
Alias: "File purged",
SystemEvent: events.ItemPurged{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFilePurged{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was removed from trashbin", "file_trash_delete")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
},
}, {
Alias: "File restored",
SystemEvent: events.ItemRestored{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
OldReference: reference("sto-123", "sto-123!iid-123/item", "./oldpath"),
Key: "",
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileRestored{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was restored from trashbin to './item'", "file_trash_restore")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
// AuditEventFileRestored fields
require.Equal(t, "./oldpath", ev.OldPath)

},
}, {
Alias: "File version restored",
SystemEvent: events.FileVersionRestored{
FileID: reference("sto-123", "iid-123", "./item"),
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
Key: "v1",
},
CheckAuditEvent: func(t *testing.T, b []byte) {
ev := types.AuditEventFileVersionRestored{}
require.NoError(t, json.Unmarshal(b, &ev))

// AuditEvent fields
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "File 'sto-123!iid-123/item' was restored in version 'v1'", "file_version_restore")
// AuditEventSharing fields
checkFilesAuditEvent(t, ev.AuditEventFiles, "sto-123!iid-123/item", "uid-123", "./item")
// AuditEventFileRestored fields
require.Equal(t, "v1", ev.Key)

},
},
}

Expand Down Expand Up @@ -343,6 +461,12 @@ func checkSharingAuditEvent(t *testing.T, ev types.AuditEventSharing, itemID str
require.Equal(t, shareID, ev.ShareID)
}

func checkFilesAuditEvent(t *testing.T, ev types.AuditEventFiles, itemID string, owner string, path string) {
require.Equal(t, itemID, ev.FileID)
require.Equal(t, owner, ev.Owner)
require.Equal(t, path, ev.Path)
}

func shareID(id string) *collaboration.ShareId {
return &collaboration.ShareId{
OpaqueId: id,
Expand Down Expand Up @@ -376,6 +500,13 @@ func resourceID(sid, oid string) *provider.ResourceId {
}
}

func reference(sid, oid, path string) *provider.Reference {
return &provider.Reference{
ResourceId: resourceID(sid, oid),
Path: path,
}
}

func timestamp(seconds uint64) *rtypes.Timestamp {
return &rtypes.Timestamp{
Seconds: seconds,
Expand All @@ -394,6 +525,7 @@ func linkPermissions(perms ...string) *link.PublicSharePermissions {
Permissions: permissions(perms...),
}
}

func permissions(permissions ...string) *provider.ResourcePermissions {
perms := &provider.ResourcePermissions{}

Expand Down
45 changes: 45 additions & 0 deletions audit/pkg/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "fmt"

// short identifiers for audit actions
const (
// Sharing
ActionShareCreated = "file_shared"
ActionSharePermissionUpdated = "share_permission_updated"
ActionShareDisplayNameUpdated = "share_name_updated"
Expand All @@ -13,6 +14,15 @@ const (
ActionShareAccepted = "share_accepted"
ActionShareDeclined = "share_declined"
ActionLinkAccessed = "public_link_accessed"

// Files
ActionFileCreated = "file_create"
ActionFileRead = "file_read"
ActionFileTrashed = "file_delete"
ActionFileRenamed = "file_rename"
ActionFilePurged = "file_trash_delete"
ActionFileRestored = "file_trash_restore"
ActionFileVersionRestored = "file_version_restore"
)

// MessageShareCreated returns the human readable string that describes the action
Expand Down Expand Up @@ -59,3 +69,38 @@ func MessageShareDeclined(userid, shareid, sharerid string) string {
func MessageLinkAccessed(linkid string, success bool) string {
return fmt.Sprintf("link '%s' was accessed. Success: %v", linkid, success)
}

// MessageFileCreated returns the human readable string that describes the action
func MessageFileCreated(item string) string {
return fmt.Sprintf("File '%s' was created", item)
}

// MessageFileRead returns the human readable string that describes the action
func MessageFileRead(item string) string {
return fmt.Sprintf("File '%s' was read", item)
}

// MessageFileTrashed returns the human readable string that describes the action
func MessageFileTrashed(item string) string {
return fmt.Sprintf("File '%s' was trashed", item)
}

// MessageFileRenamed returns the human readable string that describes the action
func MessageFileRenamed(item, oldpath, newpath string) string {
return fmt.Sprintf("File '%s' was moved from '%s' to '%s'", item, oldpath, newpath)
}

// MessageFilePurged returns the human readable string that describes the action
func MessageFilePurged(item string) string {
return fmt.Sprintf("File '%s' was removed from trashbin", item)
}

// MessageFileRestored returns the human readable string that describes the action
func MessageFileRestored(item, path string) string {
return fmt.Sprintf("File '%s' was restored from trashbin to '%s'", item, path)
}

// MessageFileVersionRestored returns the human readable string that describes the action
func MessageFileVersionRestored(item string, version string) string {
return fmt.Sprintf("File '%s' was restored in version '%s'", item, version)
}
104 changes: 104 additions & 0 deletions audit/pkg/types/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"time"

"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/utils"

group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
)

Expand Down Expand Up @@ -220,6 +222,94 @@ func LinkAccessFailed(ev events.LinkAccessFailed) AuditEventLinkAccessed {
}
}

// FilesAuditEvent creates an AuditEventFiles from the given values
func FilesAuditEvent(base AuditEvent, itemid, owner, path string) AuditEventFiles {
return AuditEventFiles{
AuditEvent: base,
FileID: itemid,
Owner: owner,
Path: path,
}
}

// FileUploaded converts a FileUploaded event to an AuditEventFileCreated
func FileUploaded(ev events.FileUploaded) AuditEventFileCreated {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)
base := BasicAuditEvent(uid, "", MessageFileCreated(iid), ActionFileCreated)
return AuditEventFileCreated{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
}
}

// FileDownloaded converts a FileDownloaded event to an AuditEventFileRead
func FileDownloaded(ev events.FileDownloaded) AuditEventFileRead {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)
base := BasicAuditEvent(uid, "", MessageFileRead(iid), ActionFileRead)
return AuditEventFileRead{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
}
}

// ItemMoved converts a ItemMoved event to an AuditEventFileRenamed
func ItemMoved(ev events.ItemMoved) AuditEventFileRenamed {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)

oldpath := ""
if ev.OldReference != nil {
oldpath = ev.OldReference.GetPath()
}

base := BasicAuditEvent(uid, "", MessageFileRenamed(iid, oldpath, path), ActionFileRenamed)
return AuditEventFileRenamed{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
OldPath: oldpath,
}
}

// ItemTrashed converts a ItemTrashed event to an AuditEventFileDeleted
func ItemTrashed(ev events.ItemTrashed) AuditEventFileDeleted {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)
base := BasicAuditEvent(uid, "", MessageFileTrashed(iid), ActionFileTrashed)
return AuditEventFileDeleted{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
}
}

// ItemPurged converts a ItemPurged event to an AuditEventFilePurged
func ItemPurged(ev events.ItemPurged) AuditEventFilePurged {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)
base := BasicAuditEvent(uid, "", MessageFilePurged(iid), ActionFilePurged)
return AuditEventFilePurged{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
}
}

// ItemRestored converts a ItemRestored event to an AuditEventFileRestored
func ItemRestored(ev events.ItemRestored) AuditEventFileRestored {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)

oldpath := ""
if ev.OldReference != nil {
oldpath = ev.OldReference.GetPath()
}

base := BasicAuditEvent(uid, "", MessageFileRestored(iid, path), ActionFileRestored)
return AuditEventFileRestored{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
OldPath: oldpath,
}
}

// FileVersionRestored converts a FileVersionRestored event to an AuditEventFileVersionRestored
func FileVersionRestored(ev events.FileVersionRestored) AuditEventFileVersionRestored {
iid, path, uid := extractFileDetails(ev.FileID, ev.Owner)
base := BasicAuditEvent(uid, "", MessageFileVersionRestored(iid, ev.Key), ActionFileVersionRestored)
return AuditEventFileVersionRestored{
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
Key: ev.Key,
}
}

func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) {
switch {
case uid != nil && uid.OpaqueId != "":
Expand All @@ -231,6 +321,20 @@ func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) {
return "", ""
}

func extractFileDetails(ref *provider.Reference, owner *user.UserId) (string, string, string) {
id, path := "", ""
if ref != nil {
path = ref.GetPath()
id, _ = utils.FormatStorageSpaceReference(ref)
}

uid := ""
if owner != nil {
uid = owner.GetOpaqueId()
}
return id, path, uid
}

func formatTime(t *types.Timestamp) string {
if t == nil {
return ""
Expand Down
7 changes: 7 additions & 0 deletions audit/pkg/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,12 @@ func RegisteredEvents() []events.Unmarshaller {
events.ReceivedShareUpdated{},
events.LinkAccessed{},
events.LinkAccessFailed{},
events.FileUploaded{},
events.FileDownloaded{},
events.ItemTrashed{},
events.ItemMoved{},
events.ItemPurged{},
events.ItemRestored{},
events.FileVersionRestored{},
}
}
Loading