diff --git a/api/types/events/metadata.go b/api/types/events/metadata.go index 8103f5ab7e94d..03ed89cab5471 100644 --- a/api/types/events/metadata.go +++ b/api/types/events/metadata.go @@ -102,3 +102,8 @@ func (m *ServerMetadata) SetServerNamespace(ns string) { func (m *SessionMetadata) GetSessionID() string { return m.SessionID } + +// GetUser returns event teleport user +func (m *UserMetadata) GetUser() string { + return m.User +} diff --git a/lib/events/athena/consumer_test.go b/lib/events/athena/consumer_test.go index 69246ee3a11f5..e226981d7619f 100644 --- a/lib/events/athena/consumer_test.go +++ b/lib/events/athena/consumer_test.go @@ -589,7 +589,7 @@ func TestErrHandlingFnFromSQS(t *testing.T) { }) } -// TestConsumerWriteToS3 is writing parquet files per date works. +// TestConsumerWriteToS3 checks if writing parquet files per date works. // It receives events from different dates and make sure that multiple // files are created and compare it against file in testdata. // Testdata files should be verified with "parquet tools" cli after changing. diff --git a/lib/events/athena/integration_test.go b/lib/events/athena/integration_test.go index 3f0a407e2d0ba..9463eda0d00c7 100644 --- a/lib/events/athena/integration_test.go +++ b/lib/events/athena/integration_test.go @@ -387,6 +387,7 @@ CREATE EXTERNAL TABLE %s ( session_id string, event_type string, event_time timestamp, + user string, event_data string ) PARTITIONED BY ( diff --git a/lib/events/athena/testdata/events_2023-04-01.parquet b/lib/events/athena/testdata/events_2023-04-01.parquet index 4c9b9f481e019..2a9f304ff2322 100644 Binary files a/lib/events/athena/testdata/events_2023-04-01.parquet and b/lib/events/athena/testdata/events_2023-04-01.parquet differ diff --git a/lib/events/athena/testdata/events_2023-04-02.parquet b/lib/events/athena/testdata/events_2023-04-02.parquet index 86ebff17303f0..96fd361151e2a 100644 Binary files a/lib/events/athena/testdata/events_2023-04-02.parquet and b/lib/events/athena/testdata/events_2023-04-02.parquet differ diff --git a/lib/events/athena/types.go b/lib/events/athena/types.go index db08d3b70ebbc..2bfc4a3b448d6 100644 --- a/lib/events/athena/types.go +++ b/lib/events/athena/types.go @@ -24,13 +24,13 @@ import ( "github.com/gravitational/teleport/lib/utils" ) -// TODO(tobiaszheller): pass user at some point. type eventParquet struct { EventType string `parquet:"name=event_type, type=BYTE_ARRAY, convertedtype=UTF8"` // TODO(tobiaszheller): what precision of timestamp we want. AWS supports micros, maybe we can use it instead of mili? EventTime int64 `parquet:"name=event_time, type=INT64, convertedtype=TIMESTAMP_MILLIS"` UID string `parquet:"name=uid, type=BYTE_ARRAY, convertedtype=UTF8"` SessionID string `parquet:"name=session_id, type=BYTE_ARRAY, convertedtype=UTF8"` + User string `parquet:"name=user, type=BYTE_ARRAY, convertedtype=UTF8"` EventData string `parquet:"name=event_data, type=BYTE_ARRAY, convertedtype=UTF8"` } @@ -49,6 +49,7 @@ func auditEventToParquet(event apievents.AuditEvent) (*eventParquet, error) { EventTime: event.GetTime().UnixMilli(), UID: event.GetID(), SessionID: events.GetSessionID(event), + User: events.GetTeleportUser(event), EventData: string(jsonBlob), }, nil } diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index edb6032811dd3..8885cc23026c2 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -47,7 +47,7 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { return s } - var eventType = getFieldEmpty(EventType) + eventType := getFieldEmpty(EventType) var e events.AuditEvent switch eventType { @@ -354,6 +354,18 @@ func GetSessionID(event events.AuditEvent) string { return sessionID } +// GetTeleportUser pulls the teleport user from the events that have a +// UserMetadata. For other events an empty string is returned. +func GetTeleportUser(event events.AuditEvent) string { + type userGetter interface { + GetUser() string + } + if g, ok := event.(userGetter); ok { + return g.GetUser() + } + return "" +} + // ToEventFields converts from the typed interface-style event representation // to the old dynamic map style representation in order to provide outer compatibility // with existing public API routes when the backend is updated with the typed events. diff --git a/lib/events/dynamic_test.go b/lib/events/dynamic_test.go index 01c964ea14944..7bda40ab1e482 100644 --- a/lib/events/dynamic_test.go +++ b/lib/events/dynamic_test.go @@ -68,3 +68,31 @@ func TestDynamicKnownType(t *testing.T) { printEvent := event.(*events.SessionPrint) require.Equal(t, SessionPrintEvent, printEvent.GetType()) } + +func TestGetTeleportUser(t *testing.T) { + tests := []struct { + name string + event events.AuditEvent + want string + }{ + { + name: "event without user metadata", + event: &events.InstanceJoin{}, + want: "", + }, + { + name: "event with user metadata", + event: &events.SessionStart{ + UserMetadata: events.UserMetadata{ + User: "user-1", + }, + }, + want: "user-1", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, GetTeleportUser(tt.event)) + }) + } +}