mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 10:12:35 +00:00
Audit: some refactoring out of types.go (#25511)
* some refactoring out of types.go * tests for metrics tag, parallelize other tests
This commit is contained in:
@@ -17,6 +17,13 @@ import (
|
||||
|
||||
var _ eventlogger.Node = (*EntryFilter)(nil)
|
||||
|
||||
// EntryFilter should be used to filter audit requests and responses which should
|
||||
// make it to a sink.
|
||||
type EntryFilter struct {
|
||||
// the evaluator for the bexpr expression that should be applied by the node.
|
||||
evaluator *bexpr.Evaluator
|
||||
}
|
||||
|
||||
// NewEntryFilter should be used to create an EntryFilter node.
|
||||
// The filter supplied should be in bexpr format and reference fields from logical.LogInputBexpr.
|
||||
func NewEntryFilter(filter string) (*EntryFilter, error) {
|
||||
|
||||
@@ -26,6 +26,14 @@ var (
|
||||
_ eventlogger.Node = (*EntryFormatter)(nil)
|
||||
)
|
||||
|
||||
// EntryFormatter should be used to format audit requests and responses.
|
||||
type EntryFormatter struct {
|
||||
salter Salter
|
||||
headerFormatter HeaderFormatter
|
||||
config FormatterConfig
|
||||
prefix string
|
||||
}
|
||||
|
||||
// NewEntryFormatter should be used to create an EntryFormatter.
|
||||
// Accepted options: WithHeaderFormatter, WithPrefix.
|
||||
func NewEntryFormatter(config FormatterConfig, salter Salter, opt ...Option) (*EntryFormatter, error) {
|
||||
|
||||
@@ -59,6 +59,8 @@ const testFormatJSONReqBasicStrFmt = `
|
||||
|
||||
// TestNewEntryFormatter ensures we can create new EntryFormatter structs.
|
||||
func TestNewEntryFormatter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
UseStaticSalt bool
|
||||
Options []Option // Only supports WithPrefix
|
||||
@@ -154,6 +156,8 @@ func TestNewEntryFormatter(t *testing.T) {
|
||||
|
||||
// TestEntryFormatter_Reopen ensures that we do not get an error when calling Reopen.
|
||||
func TestEntryFormatter_Reopen(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ss := newStaticSalt(t)
|
||||
cfg, err := NewFormatterConfig()
|
||||
require.NoError(t, err)
|
||||
@@ -166,6 +170,8 @@ func TestEntryFormatter_Reopen(t *testing.T) {
|
||||
|
||||
// TestEntryFormatter_Type ensures that the node is a 'formatter' type.
|
||||
func TestEntryFormatter_Type(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ss := newStaticSalt(t)
|
||||
cfg, err := NewFormatterConfig()
|
||||
require.NoError(t, err)
|
||||
@@ -179,6 +185,8 @@ func TestEntryFormatter_Type(t *testing.T) {
|
||||
// TestEntryFormatter_Process attempts to run the Process method to convert the
|
||||
// logical.LogInput within an audit event to JSON and JSONx (RequestEntry or ResponseEntry).
|
||||
func TestEntryFormatter_Process(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
IsErrorExpected bool
|
||||
ExpectedErrorMessage string
|
||||
@@ -409,6 +417,8 @@ func BenchmarkAuditFileSink_Process(b *testing.B) {
|
||||
// TestEntryFormatter_FormatRequest exercises EntryFormatter.FormatRequest with
|
||||
// varying inputs.
|
||||
func TestEntryFormatter_FormatRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Input *logical.LogInput
|
||||
IsErrorExpected bool
|
||||
@@ -476,6 +486,8 @@ func TestEntryFormatter_FormatRequest(t *testing.T) {
|
||||
// TestEntryFormatter_FormatResponse exercises EntryFormatter.FormatResponse with
|
||||
// varying inputs.
|
||||
func TestEntryFormatter_FormatResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Input *logical.LogInput
|
||||
IsErrorExpected bool
|
||||
@@ -543,6 +555,8 @@ func TestEntryFormatter_FormatResponse(t *testing.T) {
|
||||
// TestEntryFormatter_Process_JSON ensures that the JSON output we get matches what
|
||||
// we expect for the specified LogInput.
|
||||
func TestEntryFormatter_Process_JSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ss := newStaticSalt(t)
|
||||
|
||||
expectedResultStr := fmt.Sprintf(testFormatJSONReqBasicStrFmt, ss.salt.GetIdentifiedHMAC("foo"))
|
||||
@@ -683,6 +697,8 @@ func TestEntryFormatter_Process_JSON(t *testing.T) {
|
||||
// TestEntryFormatter_Process_JSONx ensures that the JSONx output we get matches what
|
||||
// we expect for the specified LogInput.
|
||||
func TestEntryFormatter_Process_JSONx(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, err := salt.NewSalt(context.Background(), nil, nil)
|
||||
require.NoError(t, err)
|
||||
tempStaticSalt := &staticSalt{salt: s}
|
||||
@@ -826,11 +842,7 @@ func TestEntryFormatter_Process_JSONx(t *testing.T) {
|
||||
// TestEntryFormatter_FormatResponse_ElideListResponses ensures that we correctly
|
||||
// elide data in responses to LIST operations.
|
||||
func TestEntryFormatter_FormatResponse_ElideListResponses(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
inputData map[string]any
|
||||
expectedData map[string]any
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
inputData map[string]any
|
||||
@@ -957,6 +969,8 @@ func TestEntryFormatter_FormatResponse_ElideListResponses(t *testing.T) {
|
||||
// TestEntryFormatter_Process_NoMutation tests that the event returned by an
|
||||
// EntryFormatter.Process method is not the same as the one that it accepted.
|
||||
func TestEntryFormatter_Process_NoMutation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the formatter node.
|
||||
cfg, err := NewFormatterConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -5,10 +5,42 @@ package audit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/internal/observability/event"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
// version defines the version of audit events.
|
||||
const version = "v0.1"
|
||||
|
||||
// Audit subtypes.
|
||||
const (
|
||||
RequestType subtype = "AuditRequest"
|
||||
ResponseType subtype = "AuditResponse"
|
||||
)
|
||||
|
||||
// Audit formats.
|
||||
const (
|
||||
JSONFormat format = "json"
|
||||
JSONxFormat format = "jsonx"
|
||||
)
|
||||
|
||||
// AuditEvent is the audit event.
|
||||
type AuditEvent struct {
|
||||
ID string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
Subtype subtype `json:"subtype"` // the subtype of the audit event.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Data *logical.LogInput `json:"data"`
|
||||
}
|
||||
|
||||
// format defines types of format audit events support.
|
||||
type format string
|
||||
|
||||
// subtype defines the type of audit event.
|
||||
type subtype string
|
||||
|
||||
// NewEvent should be used to create an audit event. The subtype field is needed
|
||||
// for audit events. It will generate an ID if no ID is supplied. Supported
|
||||
// options: WithID, WithNow.
|
||||
@@ -99,13 +131,14 @@ func (f format) String() string {
|
||||
}
|
||||
|
||||
// MetricTag returns a tag corresponding to this subtype to include in metrics.
|
||||
func (st subtype) MetricTag() string {
|
||||
switch st {
|
||||
// If a tag cannot be found the value is returned 'as-is' in string format.
|
||||
func (t subtype) MetricTag() string {
|
||||
switch t {
|
||||
case RequestType:
|
||||
return "log_request"
|
||||
case ResponseType:
|
||||
return "log_response"
|
||||
}
|
||||
|
||||
return ""
|
||||
return string(t)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
// TestAuditEvent_new exercises the newEvent func to create audit events.
|
||||
func TestAuditEvent_new(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Options []Option
|
||||
Subtype subtype
|
||||
@@ -107,6 +109,8 @@ func TestAuditEvent_new(t *testing.T) {
|
||||
|
||||
// TestAuditEvent_Validate exercises the validation for an audit event.
|
||||
func TestAuditEvent_Validate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value *AuditEvent
|
||||
IsErrorExpected bool
|
||||
@@ -198,6 +202,8 @@ func TestAuditEvent_Validate(t *testing.T) {
|
||||
|
||||
// TestAuditEvent_Validate_Subtype exercises the validation for an audit event's subtype.
|
||||
func TestAuditEvent_Validate_Subtype(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -243,6 +249,8 @@ func TestAuditEvent_Validate_Subtype(t *testing.T) {
|
||||
|
||||
// TestAuditEvent_Validate_Format exercises the validation for an audit event's format.
|
||||
func TestAuditEvent_Validate_Format(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -285,3 +293,42 @@ func TestAuditEvent_Validate_Format(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuditEvent_Subtype_MetricTag is used to ensure that we get the string value
|
||||
// we expect for a subtype when we want to use it as a metrics tag.
|
||||
// In some strange scenario where the subtype was never validated, it is technically
|
||||
// possible to get a value that isn't related to request/response, but this shouldn't
|
||||
// really be happening, so we will return it as is.
|
||||
func TestAuditEvent_Subtype_MetricTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
}{
|
||||
"request": {
|
||||
input: "AuditRequest",
|
||||
expectedOutput: "log_request",
|
||||
},
|
||||
"response": {
|
||||
input: "AuditResponse",
|
||||
expectedOutput: "log_response",
|
||||
},
|
||||
"non-validated": {
|
||||
input: "juan",
|
||||
expectedOutput: "juan",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
name := name
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
st := subtype(tc.input)
|
||||
tag := st.MetricTag()
|
||||
require.Equal(t, tc.expectedOutput, tag)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ func TestProcessManual_NilData(t *testing.T) {
|
||||
// TestProcessManual_BadIDs tests ProcessManual when different bad values are
|
||||
// supplied for the ID parameter.
|
||||
func TestProcessManual_BadIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
IDs []eventlogger.NodeID
|
||||
ExpectedErrorMessage string
|
||||
|
||||
@@ -10,6 +10,23 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Option is how options are passed as arguments.
|
||||
type Option func(*options) error
|
||||
|
||||
// options are used to represent configuration for a audit related nodes.
|
||||
type options struct {
|
||||
withID string
|
||||
withNow time.Time
|
||||
withSubtype subtype
|
||||
withFormat format
|
||||
withPrefix string
|
||||
withRaw bool
|
||||
withElision bool
|
||||
withOmitTime bool
|
||||
withHMACAccessor bool
|
||||
withHeaderFormatter HeaderFormatter
|
||||
}
|
||||
|
||||
// getDefaultOptions returns options with their default values.
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
|
||||
// TestOptions_WithFormat exercises WithFormat Option to ensure it performs as expected.
|
||||
func TestOptions_WithFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -68,6 +70,8 @@ func TestOptions_WithFormat(t *testing.T) {
|
||||
|
||||
// TestOptions_WithSubtype exercises WithSubtype Option to ensure it performs as expected.
|
||||
func TestOptions_WithSubtype(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -113,6 +117,8 @@ func TestOptions_WithSubtype(t *testing.T) {
|
||||
|
||||
// TestOptions_WithNow exercises WithNow Option to ensure it performs as expected.
|
||||
func TestOptions_WithNow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value time.Time
|
||||
IsErrorExpected bool
|
||||
@@ -154,6 +160,8 @@ func TestOptions_WithNow(t *testing.T) {
|
||||
|
||||
// TestOptions_WithID exercises WithID Option to ensure it performs as expected.
|
||||
func TestOptions_WithID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -199,6 +207,8 @@ func TestOptions_WithID(t *testing.T) {
|
||||
|
||||
// TestOptions_WithPrefix exercises WithPrefix Option to ensure it performs as expected.
|
||||
func TestOptions_WithPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value string
|
||||
IsErrorExpected bool
|
||||
@@ -244,6 +254,8 @@ func TestOptions_WithPrefix(t *testing.T) {
|
||||
|
||||
// TestOptions_WithRaw exercises WithRaw Option to ensure it performs as expected.
|
||||
func TestOptions_WithRaw(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value bool
|
||||
ExpectedValue bool
|
||||
@@ -274,6 +286,8 @@ func TestOptions_WithRaw(t *testing.T) {
|
||||
|
||||
// TestOptions_WithElision exercises WithElision Option to ensure it performs as expected.
|
||||
func TestOptions_WithElision(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value bool
|
||||
ExpectedValue bool
|
||||
@@ -304,6 +318,8 @@ func TestOptions_WithElision(t *testing.T) {
|
||||
|
||||
// TestOptions_WithHMACAccessor exercises WithHMACAccessor Option to ensure it performs as expected.
|
||||
func TestOptions_WithHMACAccessor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value bool
|
||||
ExpectedValue bool
|
||||
@@ -334,6 +350,8 @@ func TestOptions_WithHMACAccessor(t *testing.T) {
|
||||
|
||||
// TestOptions_WithOmitTime exercises WithOmitTime Option to ensure it performs as expected.
|
||||
func TestOptions_WithOmitTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value bool
|
||||
ExpectedValue bool
|
||||
@@ -365,6 +383,8 @@ func TestOptions_WithOmitTime(t *testing.T) {
|
||||
// TestOptions_WithHeaderFormatter exercises the WithHeaderFormatter Option to
|
||||
// ensure it applies the option as expected under various circumstances.
|
||||
func TestOptions_WithHeaderFormatter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
Value HeaderFormatter
|
||||
ExpectedValue HeaderFormatter
|
||||
@@ -403,6 +423,8 @@ func TestOptions_WithHeaderFormatter(t *testing.T) {
|
||||
|
||||
// TestOptions_Default exercises getDefaultOptions to assert the default values.
|
||||
func TestOptions_Default(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := getDefaultOptions()
|
||||
require.NotNil(t, opts)
|
||||
require.True(t, time.Now().After(opts.withNow))
|
||||
@@ -411,6 +433,8 @@ func TestOptions_Default(t *testing.T) {
|
||||
|
||||
// TestOptions_Opts exercises GetOpts with various Option values.
|
||||
func TestOptions_Opts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
opts []Option
|
||||
IsErrorExpected bool
|
||||
|
||||
@@ -6,61 +6,12 @@ package audit
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-bexpr"
|
||||
"github.com/hashicorp/vault/internal/observability/event"
|
||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
// Audit subtypes.
|
||||
const (
|
||||
RequestType subtype = "AuditRequest"
|
||||
ResponseType subtype = "AuditResponse"
|
||||
)
|
||||
|
||||
// Audit formats.
|
||||
const (
|
||||
JSONFormat format = "json"
|
||||
JSONxFormat format = "jsonx"
|
||||
)
|
||||
|
||||
// version defines the version of audit events.
|
||||
const version = "v0.1"
|
||||
|
||||
// subtype defines the type of audit event.
|
||||
type subtype string
|
||||
|
||||
// format defines types of format audit events support.
|
||||
type format string
|
||||
|
||||
// AuditEvent is the audit event.
|
||||
type AuditEvent struct {
|
||||
ID string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
Subtype subtype `json:"subtype"` // the subtype of the audit event.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Data *logical.LogInput `json:"data"`
|
||||
}
|
||||
|
||||
// Option is how options are passed as arguments.
|
||||
type Option func(*options) error
|
||||
|
||||
// options are used to represent configuration for a audit related nodes.
|
||||
type options struct {
|
||||
withID string
|
||||
withNow time.Time
|
||||
withSubtype subtype
|
||||
withFormat format
|
||||
withPrefix string
|
||||
withRaw bool
|
||||
withElision bool
|
||||
withOmitTime bool
|
||||
withHMACAccessor bool
|
||||
withHeaderFormatter HeaderFormatter
|
||||
}
|
||||
|
||||
// Salter is an interface that provides a way to obtain a Salt for hashing.
|
||||
type Salter interface {
|
||||
// Salt returns a non-nil salt or an error.
|
||||
@@ -94,14 +45,6 @@ type HeaderFormatter interface {
|
||||
ApplyConfig(context.Context, map[string][]string, Salter) (map[string][]string, error)
|
||||
}
|
||||
|
||||
// EntryFormatter should be used to format audit requests and responses.
|
||||
type EntryFormatter struct {
|
||||
salter Salter
|
||||
headerFormatter HeaderFormatter
|
||||
config FormatterConfig
|
||||
prefix string
|
||||
}
|
||||
|
||||
// EntryFormatterWriter should be used to format and write out audit requests and responses.
|
||||
type EntryFormatterWriter struct {
|
||||
Formatter
|
||||
@@ -144,13 +87,6 @@ type FormatterConfig struct {
|
||||
RequiredFormat format
|
||||
}
|
||||
|
||||
// EntryFilter should be used to filter audit requests and responses which should
|
||||
// make it to a sink.
|
||||
type EntryFilter struct {
|
||||
// the evaluator for the bexpr expression that should be applied by the node.
|
||||
evaluator *bexpr.Evaluator
|
||||
}
|
||||
|
||||
// RequestEntry is the structure of a request audit log entry.
|
||||
type RequestEntry struct {
|
||||
Time string `json:"time,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user