mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 14:47:01 +02:00
334 lines
8.9 KiB
Go
334 lines
8.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package audit
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/eventlogger"
|
|
"github.com/hashicorp/go-uuid"
|
|
nshelper "github.com/hashicorp/vault/helper/namespace"
|
|
"github.com/hashicorp/vault/internal/observability/event"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestProcessManual_NilData tests processManual when nil data is supplied.
|
|
func TestProcessManual_NilData(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Sink node
|
|
sinkId, sinkNode := newSinkNode(t)
|
|
ids = append(ids, sinkId)
|
|
nodes[sinkId] = sinkNode
|
|
|
|
err := processManual(nshelper.RootContext(context.Background()), nil, ids, nodes)
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, "data cannot be nil")
|
|
}
|
|
|
|
// 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
|
|
}{
|
|
"nil": {
|
|
IDs: nil,
|
|
ExpectedErrorMessage: "minimum of 2 ids are required",
|
|
},
|
|
"one": {
|
|
IDs: []eventlogger.NodeID{"1"},
|
|
ExpectedErrorMessage: "minimum of 2 ids are required",
|
|
},
|
|
}
|
|
|
|
for name, tc := range tests {
|
|
name := name
|
|
tc := tc
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Sink node
|
|
sinkId, sinkNode := newSinkNode(t)
|
|
nodes[sinkId] = sinkNode
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, tc.IDs, nodes)
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, tc.ExpectedErrorMessage)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestProcessManual_NoNodes tests processManual when no nodes are supplied.
|
|
func TestProcessManual_NoNodes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, _ := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
|
|
// Sink node
|
|
sinkId, _ := newSinkNode(t)
|
|
ids = append(ids, sinkId)
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, "nodes are required")
|
|
}
|
|
|
|
// TestProcessManual_IdNodeMismatch tests processManual when IDs don't match with
|
|
// the nodes in the supplied map.
|
|
func TestProcessManual_IdNodeMismatch(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Sink node
|
|
sinkId, _ := newSinkNode(t)
|
|
ids = append(ids, sinkId)
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "node not found: ")
|
|
}
|
|
|
|
// TestProcessManual_NotEnoughNodes tests processManual when there is only one
|
|
// node provided.
|
|
func TestProcessManual_NotEnoughNodes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, "minimum of 2 ids are required")
|
|
}
|
|
|
|
// TestProcessManual_LastNodeNotSink tests processManual when the last node is
|
|
// not a Sink node.
|
|
func TestProcessManual_LastNodeNotSink(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Another Formatter node
|
|
formatterId, formatterNode = newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, "last node must be a filter or sink")
|
|
}
|
|
|
|
// TestProcessManualEndWithSink ensures that the manual processing of a test
|
|
// message works as expected with proper inputs, which mean processing ends with
|
|
// sink node.
|
|
func TestProcessManualEndWithSink(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Sink node
|
|
sinkId, sinkNode := newSinkNode(t)
|
|
ids = append(ids, sinkId)
|
|
nodes[sinkId] = sinkNode
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// TestProcessManual_EndWithFilter ensures that the manual processing of a test
|
|
// message works as expected with proper inputs, which mean processing ends with
|
|
// sink node.
|
|
func TestProcessManual_EndWithFilter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var ids []eventlogger.NodeID
|
|
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
|
|
|
|
// Filter node
|
|
filterId, filterNode := newFilterNode(t)
|
|
ids = append(ids, filterId)
|
|
nodes[filterId] = filterNode
|
|
|
|
// Formatter node
|
|
formatterId, formatterNode := newFormatterNode(t)
|
|
ids = append(ids, formatterId)
|
|
nodes[formatterId] = formatterNode
|
|
|
|
// Sink node
|
|
sinkId, sinkNode := newSinkNode(t)
|
|
ids = append(ids, sinkId)
|
|
nodes[sinkId] = sinkNode
|
|
|
|
// Data
|
|
requestId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
data := newData(requestId)
|
|
|
|
err = processManual(nshelper.RootContext(context.Background()), data, ids, nodes)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// newSinkNode creates a new UUID and NoopSink (sink node).
|
|
func newSinkNode(t *testing.T) (eventlogger.NodeID, *event.NoopSink) {
|
|
t.Helper()
|
|
|
|
sinkId, err := event.GenerateNodeID()
|
|
require.NoError(t, err)
|
|
sinkNode := event.NewNoopSink()
|
|
|
|
return sinkId, sinkNode
|
|
}
|
|
|
|
// TestFilter is a trivial implementation of eventlogger.Node used as a placeholder
|
|
// for Filter nodes in tests.
|
|
type TestFilter struct{}
|
|
|
|
// Process trivially filters the event preventing it from being processed by subsequent nodes.
|
|
func (f *TestFilter) Process(_ context.Context, e *eventlogger.Event) (*eventlogger.Event, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// Reopen does nothing.
|
|
func (f *TestFilter) Reopen() error {
|
|
return nil
|
|
}
|
|
|
|
// Type returns the eventlogger.NodeTypeFormatter type.
|
|
func (f *TestFilter) Type() eventlogger.NodeType {
|
|
return eventlogger.NodeTypeFilter
|
|
}
|
|
|
|
// TestFormatter is a trivial implementation of the eventlogger.Node interface
|
|
// used as a place-holder for Formatter nodes in tests.
|
|
type TestFormatter struct{}
|
|
|
|
// Process trivially formats the event by storing "test" as a byte slice under
|
|
// the test format type.
|
|
func (f *TestFormatter) Process(_ context.Context, e *eventlogger.Event) (*eventlogger.Event, error) {
|
|
e.FormattedAs("test", []byte("test"))
|
|
|
|
return e, nil
|
|
}
|
|
|
|
// Reopen does nothing.
|
|
func (f *TestFormatter) Reopen() error {
|
|
return nil
|
|
}
|
|
|
|
// Type returns the eventlogger.NodeTypeFormatter type.
|
|
func (f *TestFormatter) Type() eventlogger.NodeType {
|
|
return eventlogger.NodeTypeFormatter
|
|
}
|
|
|
|
// newFilterNode creates a new TestFormatter (filter node).
|
|
func newFilterNode(t *testing.T) (eventlogger.NodeID, *TestFilter) {
|
|
nodeId, err := event.GenerateNodeID()
|
|
require.NoError(t, err)
|
|
node := &TestFilter{}
|
|
|
|
return nodeId, node
|
|
}
|
|
|
|
// newFormatterNode creates a new TestFormatter (formatter node).
|
|
func newFormatterNode(t *testing.T) (eventlogger.NodeID, *TestFormatter) {
|
|
nodeId, err := event.GenerateNodeID()
|
|
require.NoError(t, err)
|
|
node := &TestFormatter{}
|
|
|
|
return nodeId, node
|
|
}
|
|
|
|
// newData creates a sample logical.LogInput to be used as data for tests.
|
|
func newData(id string) *logical.LogInput {
|
|
return &logical.LogInput{
|
|
Type: "request",
|
|
Auth: nil,
|
|
Request: &logical.Request{
|
|
ID: id,
|
|
Operation: "update",
|
|
Path: "sys/audit/test",
|
|
},
|
|
Response: nil,
|
|
OuterErr: nil,
|
|
}
|
|
}
|