From 9f312f48b5567f284a5226d6d6ddc8f6b432c341 Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Mon, 8 Jul 2024 22:05:14 +0200 Subject: [PATCH] [MM-58004] Update logged fields of users (#26860) --- server/channels/audit/audit_test.go | 2 +- server/public/model/user.go | 34 ++++-- server/public/model/user_test.go | 154 +++++++++++++++++++++++++++- 3 files changed, 180 insertions(+), 10 deletions(-) diff --git a/server/channels/audit/audit_test.go b/server/channels/audit/audit_test.go index b79d5bbd55..c46d2a8155 100644 --- a/server/channels/audit/audit_test.go +++ b/server/channels/audit/audit_test.go @@ -55,7 +55,7 @@ func TestAudit_LogRecord(t *testing.T) { audit.LogRecord(mlog.LvlAuditAPI, rec) }, []string{ - strings.Replace(`{"timestamp":0,"level":"audit-api","msg":"","event_name":"User.Update","status":"success","actor":{"user_id":"","session_id":"","client":"","ip_address":"","x_forwarded_for":""},"event":{"parameters":null,"prior_state":{"allow_marketing":false,"auth_service":"","bot_description":"","bot_last_icon_update":0,"create_at":0,"delete_at":0,"disable_welcome_email":false,"email":"","email_verified":false,"failed_attempts":0,"id":"_____USERID_____","is_bot":false,"last_activity_at":0,"last_password_update":0,"last_picture_update":0,"locale":"","mfa_active":false,"notify_props":null,"position":"","props":null,"remote_id":null,"roles":"","terms_of_service_create_at":0,"terms_of_service_id":"","timezone":null,"update_at":0,"username":"TestABC"},"resulting_state":{"allow_marketing":false,"auth_service":"","bot_description":"","bot_last_icon_update":0,"create_at":0,"delete_at":0,"disable_welcome_email":false,"email":"","email_verified":false,"failed_attempts":0,"id":"_____USERID_____","is_bot":false,"last_activity_at":0,"last_password_update":0,"last_picture_update":0,"locale":"","mfa_active":false,"notify_props":null,"position":"","props":null,"remote_id":null,"roles":"","terms_of_service_create_at":0,"terms_of_service_id":"","timezone":null,"update_at":0,"username":"TestDEF"},"object_type":"user"},"meta":null,"error":{}}`, "_____USERID_____", userId, -1), + strings.Replace(`{"timestamp":0,"level":"audit-api","msg":"","event_name":"User.Update","status":"success","actor":{"user_id":"","session_id":"","client":"","ip_address":"","x_forwarded_for":""},"event":{"parameters":null,"prior_state":{"allow_marketing":false,"auth_service":"","bot_description":"","bot_last_icon_update":0,"create_at":0,"delete_at":0,"disable_welcome_email":false,"email":"","email_verified":false,"failed_attempts":0,"id":"_____USERID_____","is_bot":false,"last_activity_at":0,"last_password_update":0,"last_picture_update":0,"locale":"","mfa_active":false,"notify_props":null,"position":"","props":null,"remote_id":"","roles":"","terms_of_service_create_at":0,"terms_of_service_id":"","timezone":null,"update_at":0,"username":"TestABC"},"resulting_state":{"allow_marketing":false,"auth_service":"","bot_description":"","bot_last_icon_update":0,"create_at":0,"delete_at":0,"disable_welcome_email":false,"email":"","email_verified":false,"failed_attempts":0,"id":"_____USERID_____","is_bot":false,"last_activity_at":0,"last_password_update":0,"last_picture_update":0,"locale":"","mfa_active":false,"notify_props":null,"position":"","props":null,"remote_id":"","roles":"","terms_of_service_create_at":0,"terms_of_service_id":"","timezone":null,"update_at":0,"username":"TestDEF"},"object_type":"user"},"meta":null,"error":{}}`, "_____USERID_____", userId, -1), }, }, } diff --git a/server/public/model/user.go b/server/public/model/user.go index 378bd807a6..5c3ad3d40b 100644 --- a/server/public/model/user.go +++ b/server/public/model/user.go @@ -132,7 +132,7 @@ func (u *User) Auditable() map[string]interface{} { "locale": u.Locale, "timezone": u.Timezone, "mfa_active": u.MfaActive, - "remote_id": u.RemoteId, + "remote_id": u.GetRemoteID(), "last_activity_at": u.LastActivityAt, "is_bot": u.IsBot, "bot_description": u.BotDescription, @@ -144,7 +144,26 @@ func (u *User) Auditable() map[string]interface{} { } func (u *User) LogClone() any { - return u.Auditable() + return map[string]interface{}{ + "id": u.Id, + "create_at": u.CreateAt, + "update_at": u.UpdateAt, + "delete_at": u.DeleteAt, + "username": u.Username, + "auth_data": u.GetAuthData(), + "auth_service": u.AuthService, + "email": u.Email, + "email_verified": u.EmailVerified, + "position": u.Position, + "roles": u.Roles, + "allow_marketing": u.AllowMarketing, + "props": u.Props, + "notify_props": u.NotifyProps, + "locale": u.Locale, + "timezone": u.Timezone, + "mfa_active": u.MfaActive, + "remote_id": u.GetRemoteID(), + } } //msgp UserMap @@ -882,15 +901,16 @@ func (u *User) GetTimezoneLocation() *time.Location { // IsRemote returns true if the user belongs to a remote cluster (has RemoteId). func (u *User) IsRemote() bool { - return u.RemoteId != nil && *u.RemoteId != "" + return SafeDereference(u.RemoteId) != "" } // GetRemoteID returns the remote id for this user or "" if not a remote user. func (u *User) GetRemoteID() string { - if u.RemoteId != nil { - return *u.RemoteId - } - return "" + return SafeDereference(u.RemoteId) +} + +func (u *User) GetAuthData() string { + return SafeDereference(u.AuthData) } // GetProp fetches a prop value by name. diff --git a/server/public/model/user_test.go b/server/public/model/user_test.go index 9292468483..df17fc3157 100644 --- a/server/public/model/user_test.go +++ b/server/public/model/user_test.go @@ -9,14 +9,164 @@ import ( "strings" "testing" - "golang.org/x/crypto/bcrypt" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/crypto/bcrypt" "github.com/mattermost/mattermost/server/public/shared/mlog" + "github.com/mattermost/mattermost/server/public/shared/timezones" ) +func TestUserAuditable(t *testing.T) { + t.Run("zero value", func(t *testing.T) { + var u User + m := u.Auditable() + require.NotNil(t, m) + assert.Equal(t, "", m["remote_id"]) + }) + + t.Run("values set", func(t *testing.T) { + id := NewId() + now := GetMillis() + u := User{ + Id: id, + CreateAt: now, + UpdateAt: now, + DeleteAt: now, + Username: "some user_name", + Password: "some password", + AuthData: NewString("some_auth_data"), + AuthService: UserAuthServiceLdap, + Email: "test@example.org", + EmailVerified: true, + Position: "some position", + Roles: strings.Join([]string{ChannelAdminRoleId, SystemManagerRoleId}, ","), + AllowMarketing: true, + Props: StringMap{ + "foo": "bar", + }, + NotifyProps: StringMap{ + "bar": "foo", + }, + Locale: DefaultLocale, + Timezone: timezones.DefaultUserTimezone(), + MfaActive: true, + RemoteId: NewString("some_remote"), + } + m := u.Auditable() + + expected := map[string]any{ + "id": id, + "create_at": now, + "update_at": now, + "delete_at": now, + "username": "some user_name", + "auth_service": "ldap", + "email": "test@example.org", + "email_verified": true, + "position": "some position", + "roles": "channel_admin,system_manager", + "allow_marketing": true, + "props": StringMap{ + "foo": "bar", + }, + "notify_props": StringMap{ + "bar": "foo", + }, + "last_password_update": int64(0), + "last_picture_update": int64(0), + "failed_attempts": 0, + "locale": "en", + "timezone": StringMap(timezones.DefaultUserTimezone()), + "mfa_active": true, + "remote_id": "some_remote", + "last_activity_at": int64(0), + "is_bot": false, + "bot_description": "", + "bot_last_icon_update": int64(0), + "terms_of_service_id": "", + "terms_of_service_create_at": int64(0), + "disable_welcome_email": false, + } + + assert.Equal(t, expected, m) + }) +} + +func TestUserLogClone(t *testing.T) { + t.Run("zero value", func(t *testing.T) { + var u User + l := u.LogClone() + require.NotNil(t, l) + + m, ok := l.(map[string]interface{}) + require.True(t, ok) + assert.Equal(t, "", m["remote_id"]) + }) + + t.Run("values set", func(t *testing.T) { + id := NewId() + now := GetMillis() + + u := User{ + Id: id, + CreateAt: now, + UpdateAt: now, + DeleteAt: now, + Username: "some user_name", + Password: "some password", + AuthData: NewString("some_auth_data"), + AuthService: UserAuthServiceLdap, + Email: "test@example.org", + EmailVerified: true, + Position: "some position", + Roles: strings.Join([]string{ChannelAdminRoleId, SystemManagerRoleId}, ","), + AllowMarketing: true, + Props: StringMap{ + "foo": "bar", + }, + NotifyProps: StringMap{ + "bar": "foo", + }, + Locale: DefaultLocale, + Timezone: timezones.DefaultUserTimezone(), + MfaActive: true, + RemoteId: NewString("some_remote"), + } + + l := u.LogClone() + m, ok := l.(map[string]interface{}) + require.True(t, ok) + + expected := map[string]any{ + "id": id, + "create_at": now, + "update_at": now, + "delete_at": now, + "username": "some user_name", + "auth_data": "some_auth_data", + "auth_service": "ldap", + "email": "test@example.org", + "email_verified": true, + "position": "some position", + "roles": "channel_admin,system_manager", + "allow_marketing": true, + "props": StringMap{ + "foo": "bar", + }, + "notify_props": StringMap{ + "bar": "foo", + }, + "locale": "en", + "timezone": StringMap(timezones.DefaultUserTimezone()), + "mfa_active": true, + "remote_id": "some_remote", + } + + assert.Equal(t, expected, m) + }) +} + func TestUserDeepCopy(t *testing.T) { id := NewId() authData := "authdata"