Files
threshold/internal/observability/event/testing.go
T
Jeff Mitchell 1782f6a4e4 Enhance worker connection security (#1641)
This stores worker connection nonces within the database to ensure
uniqueness of connections. Prior the connection required any attacker to
be able to decrypt the incoming encrypted authentication message in
order to replay (and if the attacker has access to the KMS you have
bigger problems), but with this change even if an attacker does have
access to the KMS key, they cannot reuse the same message. (If they're
able to encrypt against the KMS key, they can still act as a worker with
a new message, so this only really helps if they only have decryption
capabilities.)
2021-10-27 20:32:07 -04:00

300 lines
7.2 KiB
Go

package event
import (
"context"
"io/ioutil"
"os"
"testing"
"time"
"github.com/hashicorp/boundary/globals"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/groups"
"github.com/hashicorp/eventlogger"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/wrapperspb"
)
// TestEnableEventing will enable eventing via it's envvar
// globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS
func TestEnableEventing(t *testing.T, enable bool) {
t.Helper()
if enable {
os.Setenv(globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS, "true")
t.Cleanup(func() {
defer os.Unsetenv(globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS)
})
} else {
os.Unsetenv(globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS)
}
}
// TestGetEventerConfig is a test accessor for the eventer's config
func TestGetEventerConfig(t *testing.T, e *Eventer) EventerConfig {
t.Helper()
return e.conf
}
// TestResetSysEventer will reset event.syseventer to an uninitialized state.
func TestResetSystEventer(t *testing.T) {
t.Helper()
sysEventerLock.Lock()
defer sysEventerLock.Unlock()
sysEventer = nil
}
type TestConfig struct {
EventerConfig EventerConfig
AllEvents *os.File
ErrorEvents *os.File
ObservationEvents *os.File
AuditEvents *os.File
}
// TestEventerConfig creates a test config and registers a cleanup func for its
// test tmp files.
func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig {
t.Helper()
require := require.New(t)
tmpAllFile, err := ioutil.TempFile("./", "tmp-all-events-"+testName)
require.NoError(err)
tmpErrFile, err := ioutil.TempFile("./", "tmp-errors-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpAllFile.Name())
os.Remove(tmpErrFile.Name())
})
opts := getOpts(opt...)
if opts.withSinkFormat == "" {
opts.withSinkFormat = JSONSinkFormat
}
c := TestConfig{
EventerConfig: EventerConfig{
ObservationsEnabled: true,
AuditEnabled: true,
Sinks: []*SinkConfig{
{
Name: "every-type-file-sink",
Type: FileSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpAllFile.Name(),
},
AuditConfig: DefaultAuditConfig(),
},
{
Name: "stderr",
Type: StderrSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
AuditConfig: DefaultAuditConfig(),
},
{
Name: "err-file-sink",
Type: FileSink,
EventTypes: []Type{ErrorType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpErrFile.Name(),
},
},
},
},
AllEvents: tmpAllFile,
ErrorEvents: tmpErrFile,
}
if opts.withAuditSink {
tmpFile, err := ioutil.TempFile("./", "tmp-audit-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "audit-file-sink",
Type: FileSink,
EventTypes: []Type{AuditType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
AuditConfig: DefaultAuditConfig(),
})
c.AuditEvents = tmpFile
}
if opts.withObservationSink {
tmpFile, err := ioutil.TempFile("./", "tmp-observation-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "err-observation-sink",
Type: FileSink,
EventTypes: []Type{ObservationType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
c.ObservationEvents = tmpFile
}
if opts.withSysSink {
tmpFile, err := ioutil.TempFile("./", "tmp-sysevents-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "err-sysevents-sink",
Type: FileSink,
EventTypes: []Type{SystemType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
}
return c
}
// TestRequestInfo provides a test RequestInfo
func TestRequestInfo(t *testing.T) *RequestInfo {
t.Helper()
return &RequestInfo{
EventId: "test-event-id",
Id: "test-request-info",
Method: "POST",
Path: "/test/request/info",
PublicId: "public-id",
}
}
func testAuth(t *testing.T) *Auth {
t.Helper()
return &Auth{
UserEmail: "test-auth@example.com",
UserName: "test-auth-user-name",
}
}
func testRequest(t *testing.T) *Request {
t.Helper()
return &Request{
Operation: "op",
Endpoint: "/group/<id>",
Details: &pbs.GetGroupRequest{
Id: "group-id",
},
}
}
func testResponse(t *testing.T) *Response {
t.Helper()
return &Response{
StatusCode: 200,
Details: &pbs.GetGroupResponse{
Item: &groups.Group{
Id: "group-id",
ScopeId: "org-id",
Name: &wrapperspb.StringValue{
Value: "group-name",
},
},
},
}
}
// TestWithBroker is an unexported and a test option for passing in an optional broker
func TestWithBroker(t *testing.T, b broker) Option {
t.Helper()
return func(o *options) {
o.withBroker = b
}
}
// TestWithObservationSink is a test option
func TestWithObservationSink(t *testing.T) Option {
t.Helper()
return func(o *options) {
o.withObservationSink = true
}
}
// TestWithAuditSink is a test option
func TestWithAuditSink(t *testing.T) Option {
t.Helper()
return func(o *options) {
o.withAuditSink = true
}
}
// TestWithSysSink is a test option
func TestWithSysSink(t *testing.T) Option {
t.Helper()
return func(o *options) {
o.withSysSink = true
}
}
// testWithSinkFormat is an unexported and a test option
func testWithSinkFormat(t *testing.T, fmt SinkFormat) Option {
t.Helper()
return func(o *options) {
o.withSinkFormat = fmt
}
}
type testMockBroker struct {
reopened bool
stopTimeAt time.Time
registeredNodeIds []eventlogger.NodeID
successThresholds map[eventlogger.EventType]int
pipelines []eventlogger.Pipeline
errorOnSend error
}
func (b *testMockBroker) Reopen(ctx context.Context) error {
b.reopened = true
return nil
}
func (b *testMockBroker) RegisterPipeline(def eventlogger.Pipeline) error {
b.pipelines = append(b.pipelines, def)
return nil
}
func (b *testMockBroker) Send(ctx context.Context, t eventlogger.EventType, payload interface{}) (eventlogger.Status, error) {
if b.errorOnSend != nil {
return eventlogger.Status{}, b.errorOnSend
}
return eventlogger.Status{}, nil
}
func (b *testMockBroker) StopTimeAt(t time.Time) {
b.stopTimeAt = t
}
func (b *testMockBroker) RegisterNode(id eventlogger.NodeID, node eventlogger.Node) error {
b.registeredNodeIds = append(b.registeredNodeIds, id)
return nil
}
func (b *testMockBroker) SetSuccessThreshold(t eventlogger.EventType, successThreshold int) error {
if b.successThresholds == nil {
b.successThresholds = map[eventlogger.EventType]int{}
}
b.successThresholds[t] = successThreshold
return nil
}