Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
87
server/boards/services/audit/audit.go
Обычный файл
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
Обычный файл
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
Обычный файл
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user