Doug Lauder
2023-03-22 17:22:27 -04:00
коммит произвёл GitHub
родитель b61c096497
Коммит c943ed6859
13276 изменённых файлов: 1695615 добавлений и 223189 удалений

87
server/boards/services/audit/audit.go Обычный файл
Просмотреть файл

@@ -0,0 +1,87 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package audit
import (
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
)
const (
DefMaxQueueSize = 1000
KeyAPIPath = "api_path"
KeyEvent = "event"
KeyStatus = "status"
KeyUserID = "user_id"
KeySessionID = "session_id"
KeyClient = "client"
KeyIPAddress = "ip_address"
KeyClusterID = "cluster_id"
KeyTeamID = "team_id"
Success = "success"
Attempt = "attempt"
Fail = "fail"
)
var (
LevelAuth = mlog.Level{ID: 1000, Name: "auth"}
LevelModify = mlog.Level{ID: 1001, Name: "mod"}
LevelRead = mlog.Level{ID: 1002, Name: "read"}
)
// Audit provides auditing service.
type Audit struct {
auditLogger *mlog.Logger
}
// NewAudit creates a new Audit instance which can be configured via `(*Audit).Configure`.
func NewAudit(options ...mlog.Option) (*Audit, error) {
logger, err := mlog.NewLogger(options...)
if err != nil {
return nil, err
}
return &Audit{
auditLogger: logger,
}, nil
}
// Configure provides a new configuration for this audit service.
// Zero or more sources of config can be provided:
//
// cfgFile - path to file containing JSON
// cfgEscaped - JSON string probably from ENV var
//
// For each case JSON containing log targets is provided. Target name collisions are resolved
// using the following precedence:
//
// cfgFile > cfgEscaped
func (a *Audit) Configure(cfgFile string, cfgEscaped string) error {
return a.auditLogger.Configure(cfgFile, cfgEscaped, nil)
}
// Shutdown shuts down the audit service after making best efforts to flush any
// remaining records.
func (a *Audit) Shutdown() error {
return a.auditLogger.Shutdown()
}
// LogRecord emits an audit record with complete info.
func (a *Audit) LogRecord(level mlog.Level, rec *Record) {
fields := make([]mlog.Field, 0, 7+len(rec.Meta))
fields = append(fields, mlog.String(KeyAPIPath, rec.APIPath))
fields = append(fields, mlog.String(KeyEvent, rec.Event))
fields = append(fields, mlog.String(KeyStatus, rec.Status))
fields = append(fields, mlog.String(KeyUserID, rec.UserID))
fields = append(fields, mlog.String(KeySessionID, rec.SessionID))
fields = append(fields, mlog.String(KeyClient, rec.Client))
fields = append(fields, mlog.String(KeyIPAddress, rec.IPAddress))
for _, meta := range rec.Meta {
fields = append(fields, mlog.Any(meta.K, meta.V))
}
a.auditLogger.Log(level, "audit "+rec.Event, fields...)
}

69
server/boards/services/audit/record.go Обычный файл
Просмотреть файл

@@ -0,0 +1,69 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package audit
import "github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
// Meta represents metadata that can be added to a audit record as name/value pairs.
type Meta struct {
K string
V interface{}
}
// FuncMetaTypeConv defines a function that can convert meta data types into something
// that serializes well for audit records.
type FuncMetaTypeConv func(val interface{}) (newVal interface{}, converted bool)
// Record provides a consistent set of fields used for all audit logging.
type Record struct {
APIPath string
Event string
Status string
UserID string
SessionID string
Client string
IPAddress string
Meta []Meta
metaConv []FuncMetaTypeConv
}
// Success marks the audit record status as successful.
func (rec *Record) Success() {
rec.Status = Success
}
// Success marks the audit record status as failed.
func (rec *Record) Fail() {
rec.Status = Fail
}
// AddMeta adds a single name/value pair to this audit record's metadata.
func (rec *Record) AddMeta(name string, val interface{}) {
if rec.Meta == nil {
rec.Meta = []Meta{}
}
// possibly convert val to something better suited for serializing
// via zero or more conversion functions.
for _, conv := range rec.metaConv {
converted, wasConverted := conv(val)
if wasConverted {
val = converted
break
}
}
lc, ok := val.(mlog.LogCloner)
if ok {
val = lc.LogClone()
}
rec.Meta = append(rec.Meta, Meta{K: name, V: val})
}
// AddMetaTypeConverter adds a function capable of converting meta field types
// into something more suitable for serialization.
func (rec *Record) AddMetaTypeConverter(f FuncMetaTypeConv) {
rec.metaConv = append(rec.metaConv, f)
}

83
server/boards/services/audit/record_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,83 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package audit
import (
"testing"
"github.com/stretchr/testify/require"
)
type bloated struct {
fld1 string
fld2 string
fld3 string
fld4 string
}
type wilted struct {
wilt1 string
}
func conv(val interface{}) (interface{}, bool) {
if b, ok := val.(*bloated); ok {
return &wilted{wilt1: b.fld1}, true
}
return val, false
}
func TestRecord_AddMeta(t *testing.T) {
type fields struct {
metaConv []FuncMetaTypeConv
}
type args struct {
name string
val interface{}
}
tests := []struct {
name string
fields fields
args args
wantWilt bool
wantVal string
}{
{name: "no converter", wantWilt: false, wantVal: "ok", fields: fields{}, args: args{name: "prop", val: "ok"}},
{name: "don't convert", wantWilt: false, wantVal: "ok", fields: fields{metaConv: []FuncMetaTypeConv{conv}}, args: args{name: "prop", val: "ok"}},
{name: "convert", wantWilt: true, wantVal: "1", fields: fields{metaConv: []FuncMetaTypeConv{conv}}, args: args{name: "prop", val: &bloated{
fld1: "1", fld2: "2", fld3: "3", fld4: "4"}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rec := &Record{
metaConv: tt.fields.metaConv,
}
rec.AddMeta(tt.args.name, tt.args.val)
// fetch the prop store in auditRecord meta data
var ok bool
var got interface{}
for _, meta := range rec.Meta {
if meta.K == "prop" {
ok = true
got = meta.V
break
}
}
require.True(t, ok)
// check if conversion was expected
val, ok := got.(*wilted)
require.Equal(t, tt.wantWilt, ok)
if ok {
// if converted to wilt then make sure field was copied
require.Equal(t, tt.wantVal, val.wilt1)
} else {
// if not converted, make sure val is unchanged
require.Equal(t, tt.wantVal, got)
}
})
}
}