[MM-62517] Add audit logs to Support Packet (#29844)

This commit is contained in:
Ben Schumacher 2025-02-10 11:32:04 +01:00 committed by GitHub
parent 4de0a2c1e6
commit 0927ecb0f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 104 additions and 44 deletions

View File

@ -12,6 +12,7 @@ import (
"path" "path"
"time" "time"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
@ -249,38 +250,50 @@ func (ps *PlatformService) GetNotificationLogFile(_ request.CTX) (*model.FileDat
} }
func (ps *PlatformService) GetAdvancedLogs(_ request.CTX) ([]*model.FileData, error) { func (ps *PlatformService) GetAdvancedLogs(_ request.CTX) ([]*model.FileData, error) {
advancedLoggingJSON := ps.Config().LogSettings.AdvancedLoggingJSON var (
if utils.IsEmptyJSON(advancedLoggingJSON) { rErr *multierror.Error
return nil, nil ret []*model.FileData
} )
cfg := make(mlog.LoggerConfiguration) for name, loggingJSON := range map[string]json.RawMessage{
err := json.Unmarshal(advancedLoggingJSON, &cfg) "LogSettings.AdvancedLoggingJSON": ps.Config().LogSettings.AdvancedLoggingJSON,
if err != nil { "NotificationLogSettings.AdvancedLoggingJSON": ps.Config().NotificationLogSettings.AdvancedLoggingJSON,
return nil, errors.Wrap(err, "invalid advanced logging configuration") "ExperimentalAuditSettings.AdvancedLoggingJSON": ps.Config().ExperimentalAuditSettings.AdvancedLoggingJSON,
} } {
if utils.IsEmptyJSON(loggingJSON) {
var ret []*model.FileData
for _, t := range cfg {
if t.Type != "file" {
continue continue
} }
var fileOption struct {
Filename string `json:"filename"` cfg := make(mlog.LoggerConfiguration)
} err := json.Unmarshal(loggingJSON, &cfg)
if err := json.Unmarshal(t.Options, &fileOption); err != nil {
return nil, errors.Wrap(err, "error decoding file target options")
}
data, err := os.ReadFile(fileOption.Filename)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to read notifcation log file at path %s", fileOption.Filename) rErr = multierror.Append(rErr, errors.Wrapf(err, "error decoding advanced logging configuration %s", name))
continue
} }
fileName := path.Base(fileOption.Filename) for _, t := range cfg {
ret = append(ret, &model.FileData{ if t.Type != "file" {
Filename: fileName, continue
Body: data, }
}) var fileOption struct {
Filename string `json:"filename"`
}
if err := json.Unmarshal(t.Options, &fileOption); err != nil {
rErr = multierror.Append(rErr, errors.Wrapf(err, "error decoding file target options in %s", name))
continue
}
data, err := os.ReadFile(fileOption.Filename)
if err != nil {
rErr = multierror.Append(rErr, errors.Wrapf(err, "failed to read advanced log file at path %s in %s", fileOption.Filename, name))
continue
}
fileName := path.Base(fileOption.Filename)
ret = append(ret, &model.FileData{
Filename: fileName,
Body: data,
})
}
} }
return ret, nil return ret, nil

View File

@ -114,7 +114,7 @@ func TestGetAdvancedLogs(t *testing.T) {
th := Setup(t) th := Setup(t)
defer th.TearDown() defer th.TearDown()
t.Run("log messanges from std and LDAP level get returned", func(t *testing.T) { t.Run("log messages from advanced logging settings get returned", func(t *testing.T) {
dir, err := os.MkdirTemp("", "logs") dir, err := os.MkdirTemp("", "logs")
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
@ -122,6 +122,7 @@ func TestGetAdvancedLogs(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
}) })
// Setup log files for each setting
optLDAP := map[string]string{ optLDAP := map[string]string{
"filename": path.Join(dir, "ldap.log"), "filename": path.Join(dir, "ldap.log"),
} }
@ -134,7 +135,14 @@ func TestGetAdvancedLogs(t *testing.T) {
dataStd, err := json.Marshal(optStd) dataStd, err := json.Marshal(optStd)
require.NoError(t, err) require.NoError(t, err)
cfg := mlog.LoggerConfiguration{ optNotif := map[string]string{
"filename": path.Join(dir, "notification.log"),
}
dataNotif, err := json.Marshal(optNotif)
require.NoError(t, err)
// LogSettings config
logCfg := mlog.LoggerConfiguration{
"ldap-file": mlog.TargetCfg{ "ldap-file": mlog.TargetCfg{
Type: "file", Type: "file",
Format: "json", Format: "json",
@ -155,34 +163,73 @@ func TestGetAdvancedLogs(t *testing.T) {
Options: dataStd, Options: dataStd,
}, },
} }
cfgData, err := json.Marshal(cfg) logCfgData, err := json.Marshal(logCfg)
require.NoError(t, err)
// NotificationLogSettings config
notifCfg := mlog.LoggerConfiguration{
"notification": mlog.TargetCfg{
Type: "file",
Format: "json",
Levels: []mlog.Level{
mlog.LvlInfo,
},
Options: dataNotif,
},
}
notifCfgData, err := json.Marshal(notifCfg)
require.NoError(t, err) require.NoError(t, err)
th.Service.UpdateConfig(func(c *model.Config) { th.Service.UpdateConfig(func(c *model.Config) {
c.LogSettings.AdvancedLoggingJSON = cfgData c.LogSettings.AdvancedLoggingJSON = logCfgData
c.NotificationLogSettings.AdvancedLoggingJSON = notifCfgData
// Audit logs are not testiable as they as part of the server, not the platform
}) })
th.Service.Logger().LogM([]mlog.Level{mlog.LvlLDAPInfo}, "Some LDAP info")
th.Service.Logger().Error("Some Error") // Write some logs and ensure they're flushed
err = th.Service.Logger().Flush() logger := th.Service.Logger()
notifLogger := th.Service.NotificationsLogger()
logger.LogM([]mlog.Level{mlog.LvlLDAPInfo}, "Some LDAP info")
logger.Error("Some Error")
notifLogger.Info("Some Notification")
// Flush both loggers and wait a bit for filesystem
err = logger.Flush()
require.NoError(t, err)
err = notifLogger.Flush()
require.NoError(t, err) require.NoError(t, err)
// Get and verify logs
fileDatas, err := th.Service.GetAdvancedLogs(th.Context) fileDatas, err := th.Service.GetAdvancedLogs(th.Context)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, fileDatas, 2) for _, fd := range fileDatas {
t.Log(fd.Filename)
}
require.Len(t, fileDatas, 3)
// Check the order of the log files // Helper to find file data by name
var ldapIndex = 0 findFile := func(name string) *model.FileData {
var stdIndex = 1 for _, fd := range fileDatas {
if fileDatas[1].Filename == "ldap.log" { if fd.Filename == name {
ldapIndex = 1 return fd
stdIndex = 0 }
}
return nil
} }
assert.Equal(t, "ldap.log", fileDatas[ldapIndex].Filename) // Check each log file
testlib.AssertLog(t, bytes.NewBuffer(fileDatas[ldapIndex].Body), mlog.LvlLDAPInfo.Name, "Some LDAP info") ldapFile := findFile("ldap.log")
require.NotNil(t, ldapFile)
testlib.AssertLog(t, bytes.NewBuffer(ldapFile.Body), mlog.LvlLDAPInfo.Name, "Some LDAP info")
assert.Equal(t, "std.log", fileDatas[stdIndex].Filename) stdFile := findFile("std.log")
testlib.AssertLog(t, bytes.NewBuffer(fileDatas[stdIndex].Body), mlog.LvlError.Name, "Some Error") require.NotNil(t, stdFile)
testlib.AssertLog(t, bytes.NewBuffer(stdFile.Body), mlog.LvlError.Name, "Some Error")
notifFile := findFile("notification.log")
require.NotNil(t, notifFile)
testlib.AssertLog(t, bytes.NewBuffer(notifFile.Body), mlog.LvlInfo.Name, "Some Notification")
}) })
// Disable AdvancedLoggingJSON // Disable AdvancedLoggingJSON
th.Service.UpdateConfig(func(c *model.Config) { th.Service.UpdateConfig(func(c *model.Config) {