diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6cd3dfcd65..9167495ccd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,3 +183,4 @@ Note that if `max_connections` is set too low, it may result in sporadic test failures if a connection cannot be established. In this case, reduce the number of concurrent tests via `GOMAXPROCS` or selectively run tests. +## [Adding additional field to an existing API (or new API)](internal/adding-a-new-field-readme.md) diff --git a/go.mod b/go.mod index 29869f0d42..c4e5177af7 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hashicorp/boundary/api v0.0.19 github.com/hashicorp/boundary/sdk v0.0.11 @@ -26,7 +26,7 @@ require ( github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 github.com/hashicorp/eventlogger v0.1.0 - github.com/hashicorp/eventlogger/filters/encrypt v0.1.4-0.20210928205053-80364fba97eb + github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v0.16.2 diff --git a/go.sum b/go.sum index c36a7696e7..69c68d2673 100644 --- a/go.sum +++ b/go.sum @@ -458,9 +458,9 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.4-0.20210928205053-80364fba97eb h1:AhprXfDoXGI8hP5fUCyCB29jpfd01Ck996ErqG0nVGk= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.4-0.20210928205053-80364fba97eb/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5-0.20211025115820-78e1ded4aea1/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= diff --git a/internal/adding-a-new-field-readme.md b/internal/adding-a-new-field-readme.md index 753309df84..96146074a6 100644 --- a/internal/adding-a-new-field-readme.md +++ b/internal/adding-a-new-field-readme.md @@ -7,13 +7,16 @@ Once you've figured out that you need an additional field in Boundary's domain m * Make schema changes: * Define the new column and provide for the migration of existing data - * Create a new migration under `internal/db/schema/migrations/postgres` - * Run `make migrations` after modifying DDL in SQL files. + + * Create a new migration under `internal/db/schema/migrations/oss/postgres` * Add the new field to the storage protobuf * storage protobufs are under: `internal/proto/local/controller/storage` - * Define a gorm tag for the new field via `@inject_tag` + + * Define a gorm tag for the new field via `@gotags` (`@inject_tag` has been deprecated) + * Define a `custom_options.v1.mask_mapping` for the field which maps the storage `this` field to the API `that` field (yes, it's the opposite of how it's defined for the API protobuf) + * Run `make proto` after modifying storage protobuf * Extend the existing repository function for Updating the resource to incorporate the new field. This could/may entail defining new options for the Update function. @@ -29,6 +32,12 @@ Now that the repository supports the new field, you can move on to adding this n * Define a `custom_options.v1.generate_sdk_option` tag for the SDK +* Define a data classification/filter tag for the field via `@gotags` or the + `encrypt.Taggable` interface which specifies how sensitive/secret/public data + will be handled for the API's audit events. Please write unit tests to verify + the audit event is properly "redacted" (see the unit tests of + `TestAuthenticate_Tags` and `TestAuthenticate` for examples). + * Run `make proto` and `make api` after modifying the API/SDK protobufs ## Update the API handler service diff --git a/internal/authtoken/repository.go b/internal/authtoken/repository.go index 3853d1901b..6f59dd416c 100644 --- a/internal/authtoken/repository.go +++ b/internal/authtoken/repository.go @@ -383,7 +383,7 @@ func (r *Repository) IssueAuthToken(ctx context.Context, tokenRequestId string) // trigger to set ApproximateLastAccessTime to the commit timestamp. rowsUpdated, err := w.Update(ctx, at, []string{"Status"}, []string{"ApproximateLastAccessTime"}, db.WithWhere("status = ?", PendingStatus)) if err != nil { - return errors.Wrap(ctx, err, op) + return errors.Wrap(ctx, err, op, errors.WithoutEvent()) } if rowsUpdated == 0 { return errors.New(ctx, errors.RecordNotFound, op, "pending auth token not found") diff --git a/internal/gen/controller/api/services/auth_method_service.pb.go b/internal/gen/controller/api/services/auth_method_service.pb.go index a22b7cb26e..ce3b85cd3a 100644 --- a/internal/gen/controller/api/services/auth_method_service.pb.go +++ b/internal/gen/controller/api/services/auth_method_service.pb.go @@ -30,7 +30,7 @@ type GetAuthMethodRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *GetAuthMethodRequest) Reset() { @@ -281,7 +281,7 @@ type CreateAuthMethodResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty" class:"public"` // @gotags: `class:"public"` Item *authmethods.AuthMethod `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` } @@ -336,7 +336,7 @@ type UpdateAuthMethodRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` Item *authmethods.AuthMethod `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"` } @@ -591,7 +591,7 @@ type ChangeStateRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` // Version is used to ensure this resource has not changed. // The mutation will fail if the version does not match the latest known good version. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -876,7 +876,7 @@ type AuthenticateRequest struct { AuthMethodId string `protobuf:"bytes,1,opt,name=auth_method_id,proto3" json:"auth_method_id,omitempty" class:"public"` // @gotags: `class:"public"` // This can be "cookie" or "token". If not provided, "token" will be used. "cookie" activates a split-cookie method where the token is split partially between http-only and regular cookies in order // to keep it safe from rogue JS in the browser. - TokenType string `protobuf:"bytes,2,opt,name=token_type,proto3" json:"token_type,omitempty"` + TokenType string `protobuf:"bytes,2,opt,name=token_type,proto3" json:"token_type,omitempty" class:"public"` // @gotags: `class:"public"` // Attributes are passed to the Auth Method; the valid keys and values depend on the type of Auth Method as well as the command. Attributes *structpb.Struct `protobuf:"bytes,4,opt,name=attributes,proto3" json:"attributes,omitempty"` // The command to perform. diff --git a/internal/gen/controller/api/services/auth_method_service_taggable.go b/internal/gen/controller/api/services/auth_method_service_taggable.go index 7359eaf3d9..c359a0bed5 100644 --- a/internal/gen/controller/api/services/auth_method_service_taggable.go +++ b/internal/gen/controller/api/services/auth_method_service_taggable.go @@ -54,6 +54,22 @@ func (req *AuthenticateResponse) Tags() ([]encrypt.PointerTag, error) { Pointer: "/Attributes/Fields/user_id", Classification: encrypt.PublicClassification, }, + { + Pointer: "/Attributes/Fields/status", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/auth_url", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/token_id", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/final_redirect_url", + Classification: encrypt.PublicClassification, + }, // secret fields { Pointer: "/Attributes/Fields/token", @@ -61,3 +77,54 @@ func (req *AuthenticateResponse) Tags() ([]encrypt.PointerTag, error) { }, }, nil } + +// Tags implements the encrypt.Taggable interface which allows +// AuthenticateRequest Attributes to be classified for the encrypt filter. +func (req *AuthenticateRequest) Tags() ([]encrypt.PointerTag, error) { + if req.Attributes == nil { + return nil, nil + } + return []encrypt.PointerTag{ + // public fields + { + Pointer: "/Attributes/Fields/login_name", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/auth_url", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/token_id", + Classification: encrypt.PublicClassification, + }, + { + Pointer: "/Attributes/Fields/state", + Classification: encrypt.PublicClassification, + }, + // secret fields + { + Pointer: "/Attributes/Fields/password", + Classification: encrypt.SecretClassification, + }, + { + Pointer: "/Attributes/Fields/code", + Classification: encrypt.SecretClassification, + }, + }, nil +} + +// Tags implements the encrypt.Taggable interface which allows +// ChangeStateRequest Attributes to be classified for the encrypt filter. +func (req *ChangeStateRequest) Tags() ([]encrypt.PointerTag, error) { + if req.Attributes == nil { + return nil, nil + } + return []encrypt.PointerTag{ + // public fields + { + Pointer: "/Attributes/Fields/state", + Classification: encrypt.PublicClassification, + }, + }, nil +} diff --git a/internal/gen/controller/api/services/auth_method_service_taggable_test.go b/internal/gen/controller/api/services/auth_method_service_taggable_test.go index aee0e8602a..0dc6fb6b60 100644 --- a/internal/gen/controller/api/services/auth_method_service_taggable_test.go +++ b/internal/gen/controller/api/services/auth_method_service_taggable_test.go @@ -1,4 +1,4 @@ -package services +package services_test import ( "context" @@ -6,23 +6,24 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/sdk/pbs/controller/api" "github.com/hashicorp/boundary/sdk/wrapper" "github.com/hashicorp/eventlogger" - "github.com/hashicorp/eventlogger/filters/encrypt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" structpb "google.golang.org/protobuf/types/known/structpb" ) -func TestScope_Tags(t *testing.T) { +// TestAuthenticate_Tags will test that the response filtering aligns with the +// AuthenticateResponse and AuthenticateResponse tags. See: +// internal/tests/api/authmethods/authenticate_test.go TestAuthenticate where +// the audit events produced using these tags is unit tested. +func TestAuthenticate_Tags(t *testing.T) { ctx := context.Background() now := time.Now() wrapper := wrapper.TestWrapper(t) - testEncryptingFilter := &encrypt.Filter{ - Wrapper: wrapper, - HmacSalt: []byte("salt"), - HmacInfo: []byte("info"), - } + testEncryptingFilter := api.NewEncryptFilter(t, wrapper) tests := []struct { name string @@ -30,11 +31,11 @@ func TestScope_Tags(t *testing.T) { wantEvent *eventlogger.Event }{ { - name: "validate-filtering", + name: "validate-response-filtering", testEvent: &eventlogger.Event{ Type: "test", CreatedAt: now, - Payload: &AuthenticateResponse{ + Payload: &services.AuthenticateResponse{ Command: "public-command", Attributes: &structpb.Struct{ Fields: map[string]*structpb.Value{ @@ -49,6 +50,10 @@ func TestScope_Tags(t *testing.T) { "token_type": structpb.NewStringValue("public-token_type"), "updated_time": structpb.NewStringValue("public-updated_time"), "user_id": structpb.NewStringValue("public-user_id"), + "status": structpb.NewStringValue("public-status"), + "auth_url": structpb.NewStringValue("public-auth_url"), + "token_id": structpb.NewStringValue("public-token_id"), + "final_redirect_url": structpb.NewStringValue("public-final_redirect_url"), "token": structpb.NewStringValue("secret-token"), }, }, @@ -57,7 +62,7 @@ func TestScope_Tags(t *testing.T) { wantEvent: &eventlogger.Event{ Type: "test", CreatedAt: now, - Payload: &AuthenticateResponse{ + Payload: &services.AuthenticateResponse{ Command: "public-command", Attributes: &structpb.Struct{ Fields: map[string]*structpb.Value{ @@ -72,12 +77,57 @@ func TestScope_Tags(t *testing.T) { "token_type": structpb.NewStringValue("public-token_type"), "updated_time": structpb.NewStringValue("public-updated_time"), "user_id": structpb.NewStringValue("public-user_id"), + "status": structpb.NewStringValue("public-status"), + "auth_url": structpb.NewStringValue("public-auth_url"), + "token_id": structpb.NewStringValue("public-token_id"), + "final_redirect_url": structpb.NewStringValue("public-final_redirect_url"), "token": structpb.NewStringValue(""), }, }, }, }, }, + { + name: "validate-request-filtering", + testEvent: &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &services.AuthenticateRequest{ + AuthMethodId: "public-auth-method-id", + TokenType: "public-token-type", + Command: "public-command", + Attributes: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "login_name": structpb.NewStringValue("public-login_name"), + "auth_url": structpb.NewStringValue("public-auth_url"), + "token_id": structpb.NewStringValue("public-token_id"), + "state": structpb.NewStringValue("public-state"), + "password": structpb.NewStringValue("secret-password"), + "code": structpb.NewStringValue("secret-code"), + }, + }, + }, + }, + wantEvent: &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &services.AuthenticateRequest{ + AuthMethodId: "public-auth-method-id", + TokenType: "public-token-type", + Command: "public-command", + Attributes: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "login_name": structpb.NewStringValue("public-login_name"), + "auth_url": structpb.NewStringValue("public-auth_url"), + "token_id": structpb.NewStringValue("public-token_id"), + "state": structpb.NewStringValue("public-state"), + "password": structpb.NewStringValue(""), + "code": structpb.NewStringValue(""), + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/observability/event/eventer.go b/internal/observability/event/eventer.go index 2943e0bc64..4bf82eab47 100644 --- a/internal/observability/event/eventer.go +++ b/internal/observability/event/eventer.go @@ -9,6 +9,7 @@ import ( "log" "net/url" "os" + "reflect" "runtime" "strings" "sync" @@ -19,8 +20,8 @@ import ( "github.com/hashicorp/eventlogger/filters/gated" "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" "github.com/hashicorp/eventlogger/sinks/writer" - "github.com/hashicorp/go-hclog" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) const ( @@ -137,6 +138,19 @@ func SysEventer() *Eventer { return sysEventer } +// NewAuditEncryptFilter returns a new encrypt filter which is initialized for +// audit events. +func NewAuditEncryptFilter(opt ...Option) (*encrypt.Filter, error) { + opts := getOpts(opt...) + + return &encrypt.Filter{ + Wrapper: opts.withAuditWrapper, + IgnoreTypes: []reflect.Type{ + reflect.TypeOf(&fieldmaskpb.FieldMask{}), + }, + }, nil +} + // NewEventer creates a new Eventer using the config. Supports options: // WithNow, WithSerializationLock, WithBroker, WithAuditWrapper func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName string, c EventerConfig, opt ...Option) (*Eventer, error) { @@ -269,8 +283,9 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } - encryptFilter := &encrypt.Filter{ - Wrapper: opts.withAuditWrapper, + encryptFilter, err := NewAuditEncryptFilter(opt...) + if err != nil { + return nil, fmt.Errorf("%s: %w", op, err) } if len(s.AuditConfig.FilterOverrides) > 0 { overrides := encrypt.DefaultFilterOperations() diff --git a/internal/proto/local/controller/api/resources/authmethods/v1/auth_method.proto b/internal/proto/local/controller/api/resources/authmethods/v1/auth_method.proto index a34480a4eb..ba4a16bfc0 100644 --- a/internal/proto/local/controller/api/resources/authmethods/v1/auth_method.proto +++ b/internal/proto/local/controller/api/resources/authmethods/v1/auth_method.proto @@ -153,36 +153,36 @@ message OidcAuthMethodAttributes { // The structure of the OIDC authenticate start response, in the JSON object message OidcAuthMethodAuthenticateStartResponse { // The returned authentication URL - string auth_url = 10 [json_name = "auth_url"]; + string auth_url = 10 [json_name = "auth_url"]; // @gotags: `class:"public"` // The returned token ID - string token_id = 30 [json_name = "token_id"]; + string token_id = 30 [json_name = "token_id"]; // @gotags: `class:"public"` } // The structure of OIDC callback request parameters message OidcAuthMethodAuthenticateCallbackRequest { // The returned code - string code = 10 [json_name = "code"]; + string code = 10 [json_name = "code"]; // @gotags: `class:"secret"` // The returned state - string state = 20 [json_name = "state"]; + string state = 20 [json_name = "state"]; // @gotags: `class:"public"` // Error parameters, if they are returned - string error = 30 [json_name = "error"]; - string error_description = 40 [json_name = "error_description"]; - string error_uri = 50 [json_name = "error_uri"]; + string error = 30 [json_name = "error"]; // @gotags: `class:"public"` + string error_description = 40 [json_name = "error_description"]; // @gotags: `class:"public"` + string error_uri = 50 [json_name = "error_uri"]; // @gotags: `class:"public"` } // The structure of OIDC callback response parameters message OidcAuthMethodAuthenticateCallbackResponse { // The final redirection URL - string final_redirect_url = 10 [json_name = "final_redirect_url"]; + string final_redirect_url = 10 [json_name = "final_redirect_url"]; // @gotags: `class:"public"` } // The structure of OIDC token request parameters message OidcAuthMethodAuthenticateTokenRequest { // The ID of the pending token - string token_id = 10 [json_name = "token_id"]; + string token_id = 10 [json_name = "token_id"]; // @gotags: `class:"private"` } // Internal only: the structure of a token response if it _does not_ contain a @@ -190,5 +190,5 @@ message OidcAuthMethodAuthenticateTokenRequest { message OidcAuthMethodAuthenticateTokenResponse { // The status. This will always be "unknown". It will never be forwarded to // the consumer. - string status = 10; + string status = 10; // @gotags: `class:"public"` } \ No newline at end of file diff --git a/internal/proto/local/controller/api/services/v1/auth_method_service.proto b/internal/proto/local/controller/api/services/v1/auth_method_service.proto index c66e0ec96c..9e2324f033 100644 --- a/internal/proto/local/controller/api/services/v1/auth_method_service.proto +++ b/internal/proto/local/controller/api/services/v1/auth_method_service.proto @@ -107,7 +107,7 @@ service AuthMethodService { } message GetAuthMethodRequest { - string id = 1; + string id = 1; // @gotags: `class:"public"` } message GetAuthMethodResponse { @@ -129,12 +129,12 @@ message CreateAuthMethodRequest { } message CreateAuthMethodResponse { - string uri = 1; + string uri = 1; // @gotags: `class:"public"` resources.authmethods.v1.AuthMethod item = 2; } message UpdateAuthMethodRequest { - string id = 1; + string id = 1; // @gotags: `class:"public"` resources.authmethods.v1.AuthMethod item = 2; google.protobuf.FieldMask update_mask = 3 [json_name = "update_mask"]; } @@ -160,7 +160,7 @@ message OidcChangeStateAttributes { } message ChangeStateRequest { - string id = 1; + string id = 1; // @gotags: `class:"public"` // Version is used to ensure this resource has not changed. // The mutation will fail if the version does not match the latest known good version. uint32 version = 2; @@ -203,7 +203,7 @@ message AuthenticateRequest { string auth_method_id = 1 [json_name = "auth_method_id"]; // @gotags: `class:"public"` // This can be "cookie" or "token". If not provided, "token" will be used. "cookie" activates a split-cookie method where the token is split partially between http-only and regular cookies in order // to keep it safe from rogue JS in the browser. - string token_type = 2 [json_name = "token_type"]; + string token_type = 2 [json_name = "token_type"]; // @gotags: `class:"public"` // Attributes are passed to the Auth Method; the valid keys and values depend on the type of Auth Method as well as the command. google.protobuf.Struct attributes = 4 [json_name = "attributes"]; // The command to perform. diff --git a/internal/tests/api/authmethods/authenticate_test.go b/internal/tests/api/authmethods/authenticate_test.go index 55ee44ed35..0cc5a1eeed 100644 --- a/internal/tests/api/authmethods/authenticate_test.go +++ b/internal/tests/api/authmethods/authenticate_test.go @@ -4,19 +4,41 @@ import ( "encoding/json" "fmt" "net/http" + "os" + "sync" "testing" "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/authmethods" "github.com/hashicorp/boundary/api/authtokens" + "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers/controller" + tests_api "github.com/hashicorp/boundary/internal/tests/api" + "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// TestAuthenticate tests the api calls and the audit events it should produce func TestAuthenticate(t *testing.T) { + // this cannot run in parallel because it relies on envvar + // globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS + event.TestEnableEventing(t, true) + assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) + eventConfig := event.TestEventerConfig(t, "TestAuthenticateAuditEntry", event.TestWithAuditSink(t)) + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + require.NoError(event.InitSysEventer(testLogger, testLock, "TestAuthenticateAuditEntry", event.WithEventerConfig(&eventConfig.EventerConfig))) + tcConfig, err := config.DevController() + require.NoError(err) + tcConfig.Eventing = &eventConfig.EventerConfig + + tc := controller.NewTestController(t, &controller.TestControllerOpts{Config: tcConfig}) defer tc.Shutdown() client := tc.Client() @@ -49,4 +71,30 @@ func TestAuthenticate(t *testing.T) { token := new(authtokens.AuthToken) require.NoError(json.Unmarshal(result.GetRawAttributes(), token)) require.NotEmpty(token.Token) + + require.NotNil(eventConfig.AuditEvents) + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls + + tok, err = methods.Authenticate(tc.Context(), tc.Server().DevPasswordAuthMethodId, "login", map[string]interface{}{"login_name": "user", "password": "passpass"}) + require.NoError(err) + assert.NotNil(tok) + got := tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + reqDetails := tests_api.GetEventDetails(t, got, "request") + tests_api.AssertRedactedValues(t, reqDetails) + tests_api.AssertRedactedValues(t, reqDetails["attributes"], "password") + + respDetails := tests_api.GetEventDetails(t, got, "response") + tests_api.AssertRedactedValues(t, respDetails) + tests_api.AssertRedactedValues(t, respDetails["attributes"], "token") + + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls + tok, err = methods.Authenticate(tc.Context(), tc.Server().DevPasswordAuthMethodId, "login", map[string]interface{}{"login_name": "user", "password": "bad-pass"}) + require.Error(err) + assert.Nil(tok) + got = tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + reqDetails = tests_api.GetEventDetails(t, got, "request") + tests_api.AssertRedactedValues(t, reqDetails) + tests_api.AssertRedactedValues(t, reqDetails["attributes"], "password") } diff --git a/internal/tests/api/authmethods/authmethod_test.go b/internal/tests/api/authmethods/authmethod_test.go index 6e54f7ddc4..4936e49c82 100644 --- a/internal/tests/api/authmethods/authmethod_test.go +++ b/internal/tests/api/authmethods/authmethod_test.go @@ -2,13 +2,19 @@ package authmethods_test import ( "net/http" + "os" + "sync" "testing" "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/authmethods" "github.com/hashicorp/boundary/internal/auth/password" + "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers/controller" + tests_api "github.com/hashicorp/boundary/internal/tests/api" capoidc "github.com/hashicorp/cap/oidc" + "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,8 +22,23 @@ import ( const global = "global" func TestCrud(t *testing.T) { + // this cannot run in parallel because it relies on envvar + // globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS + event.TestEnableEventing(t, true) + assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) + eventConfig := event.TestEventerConfig(t, "TestCrud", event.TestWithAuditSink(t)) + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + require.NoError(event.InitSysEventer(testLogger, testLock, "TestCrud", event.WithEventerConfig(&eventConfig.EventerConfig))) + tcConfig, err := config.DevController() + require.NoError(err) + tcConfig.Eventing = &eventConfig.EventerConfig + + tc := controller.NewTestController(t, &controller.TestControllerOpts{Config: tcConfig}) defer tc.Shutdown() client := tc.Client() @@ -35,18 +56,44 @@ func TestCrud(t *testing.T) { assert.EqualValues(wantedVersion, u.Version) } + require.NotNil(eventConfig.AuditEvents) + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls u, err := amClient.Create(tc.Context(), "password", global, authmethods.WithName("bar")) require.NoError(err) checkAuthMethod("create", u.Item, "bar", 1) + got := tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + reqItem := tests_api.GetEventDetails(t, got, "request")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, reqItem) + + respItem := tests_api.GetEventDetails(t, got, "response")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, respItem) + tests_api.AssertRedactedValues(t, respItem["attributes"]) + + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls u, err = amClient.Read(tc.Context(), u.Item.Id) require.NoError(err) checkAuthMethod("read", u.Item, "bar", 1) + got = tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + tests_api.AssertRedactedValues(t, tests_api.GetEventDetails(t, got, "request")) + + respItem = tests_api.GetEventDetails(t, got, "response")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, respItem) + tests_api.AssertRedactedValues(t, respItem["attributes"]) + + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls u, err = amClient.Update(tc.Context(), u.Item.Id, u.Item.Version, authmethods.WithName("buz")) require.NoError(err) checkAuthMethod("update", u.Item, "buz", 2) + got = tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + tests_api.AssertRedactedValues(t, tests_api.GetEventDetails(t, got, "request")["item"].(map[string]interface{})) + + respItem = tests_api.GetEventDetails(t, got, "response")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, respItem) + tests_api.AssertRedactedValues(t, respItem["attributes"]) u, err = amClient.Update(tc.Context(), u.Item.Id, u.Item.Version, authmethods.DefaultName()) require.NoError(err) @@ -55,6 +102,7 @@ func TestCrud(t *testing.T) { _, err = amClient.Delete(tc.Context(), u.Item.Id) require.NoError(err) + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls // OIDC auth methods u, err = amClient.Create(tc.Context(), "oidc", global, authmethods.WithName("foo"), @@ -64,6 +112,15 @@ func TestCrud(t *testing.T) { authmethods.WithOidcAuthMethodClientId("client-id")) require.NoError(err) checkAuthMethod("create", u.Item, "foo", 1) + got = tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + reqItem = tests_api.GetEventDetails(t, got, "request")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, reqItem) + tests_api.AssertRedactedValues(t, reqItem["attributes"], "client_secret") + + respItem = tests_api.GetEventDetails(t, got, "response")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, respItem) + tests_api.AssertRedactedValues(t, respItem["attributes"]) u, err = amClient.Read(tc.Context(), u.Item.Id) require.NoError(err) @@ -82,7 +139,23 @@ func TestCrud(t *testing.T) { } func TestCustomMethods(t *testing.T) { - tc := controller.NewTestController(t, nil) + // this cannot run in parallel because it relies on envvar + // globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS + event.TestEnableEventing(t, true) + + assert, require := assert.New(t), require.New(t) + eventConfig := event.TestEventerConfig(t, "TestCrud", event.TestWithAuditSink(t)) + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + require.NoError(event.InitSysEventer(testLogger, testLock, "TestCrud", event.WithEventerConfig(&eventConfig.EventerConfig))) + tcConfig, err := config.DevController() + require.NoError(err) + tcConfig.Eventing = &eventConfig.EventerConfig + + tc := controller.NewTestController(t, &controller.TestControllerOpts{Config: tcConfig}) defer tc.Shutdown() client := tc.Client() @@ -104,20 +177,30 @@ func TestCustomMethods(t *testing.T) { authmethods.WithOidcAuthMethodClientId("client-id"), authmethods.WithOidcAuthMethodSigningAlgorithms([]string{string("EdDSA")}), authmethods.WithOidcAuthMethodIdpCaCerts([]string{tp.CACert()})) - require.NoError(t, err) + require.NoError(err) const newState = "active-private" nilU, err := amClient.ChangeState(tc.Context(), u.Item.Id, u.Item.Version, newState) - require.Error(t, err) - assert.Nil(t, nilU) + require.Error(err) + assert.Nil(nilU) + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls u, err = amClient.ChangeState(tc.Context(), u.Item.Id, u.Item.Version, newState, authmethods.WithOidcAuthMethodDisableDiscoveredConfigValidation(true)) - require.NoError(t, err) - assert.NotNil(t, u) - assert.Equal(t, newState, u.Item.Attributes["state"]) + require.NoError(err) + assert.NotNil(u) + assert.Equal(newState, u.Item.Attributes["state"]) + got := tests_api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + reqDetails := tests_api.GetEventDetails(t, got, "request") + tests_api.AssertRedactedValues(t, reqDetails) + tests_api.AssertRedactedValues(t, reqDetails["attributes"]) + + respItem := tests_api.GetEventDetails(t, got, "response")["item"].(map[string]interface{}) + tests_api.AssertRedactedValues(t, respItem) + tests_api.AssertRedactedValues(t, respItem["attributes"]) _, err = amClient.ChangeState(tc.Context(), u.Item.Id, u.Item.Version, "", authmethods.WithOidcAuthMethodDisableDiscoveredConfigValidation(true)) - assert.Error(t, err) + assert.Error(err) } func TestErrors(t *testing.T) { diff --git a/internal/tests/api/testing.go b/internal/tests/api/testing.go new file mode 100644 index 0000000000..0a561f4710 --- /dev/null +++ b/internal/tests/api/testing.go @@ -0,0 +1,80 @@ +package api + +import ( + "encoding/json" + "io/ioutil" + "testing" + + "github.com/hashicorp/eventlogger/filters/encrypt" + "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// CloudEventFromFile will marshal a single cloud event from the provided file +// name +func CloudEventFromFile(t *testing.T, fileName string) *cloudevents.Event { + t.Helper() + b, err := ioutil.ReadFile(fileName) + assert.NoError(t, err) + got := &cloudevents.Event{} + err = json.Unmarshal(b, got) + require.NoErrorf(t, err, "json: %s", string(b)) + return got +} + +// GetEventDetails is a testing helper will return the details from the event +// payload for a given messageType (request or response) +func GetEventDetails(t *testing.T, e *cloudevents.Event, messageType string) map[string]interface{} { + t.Helper() + require := require.New(t) + require.NotNil(e) + require.NotEmpty(messageType) + data, ok := e.Data.(map[string]interface{}) + if !ok { + return nil + } + msgType, ok := data[messageType].(map[string]interface{}) + if !ok { + return nil + } + + details, ok := msgType["details"].(map[string]interface{}) + if !ok { + return nil + } + return details +} + +// AssertRedactedValues will assert that the values for the given keys within +// the data have been redacted +func AssertRedactedValues(t *testing.T, data interface{}, keys ...string) { + t.Helper() + assert, require := assert.New(t), require.New(t) + require.NotNil(data) + dataMap, ok := data.(map[string]interface{}) + require.Truef(ok, "data must be a map[string]interface{}") + + rMap := make(map[string]bool, len(keys)) + for _, s := range keys { + rMap[s] = true + } + for k, v := range dataMap { + switch typ := v.(type) { + case []interface{}: + for _, s := range typ { + if _, ok := rMap[k]; ok { + assert.Equalf(encrypt.RedactedData, s, "expected %s to be redacted and it was set to: %s", k, v) + } else { + assert.NotEqualf(encrypt.RedactedData, s, "did not expect %s to be redacted", k) + } + } + default: + if _, ok := rMap[k]; ok { + assert.Equalf(encrypt.RedactedData, v, "expected %s to be redacted and it was set to: %s", k, v) + } else { + assert.NotEqualf(encrypt.RedactedData, v, "did not expect %s to be redacted", k) + } + } + } +} diff --git a/sdk/go.mod b/sdk/go.mod index 30ef008c52..e4ec45cf97 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -7,7 +7,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/hashicorp/eventlogger v0.1.0 - github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 + github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-kms-wrapping v0.6.6 github.com/hashicorp/go-retryablehttp v0.7.0 // indirect diff --git a/sdk/go.sum b/sdk/go.sum index 08db5d8e00..ce2eba2626 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -237,8 +237,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3 h1:RKYUGplnpDoE/Cj2vupQqUJS/wWBVvmpxEYeEpGL88s= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.3/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= diff --git a/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go b/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go index 24176d980a..ea7612a1ae 100644 --- a/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go +++ b/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go @@ -452,9 +452,9 @@ type OidcAuthMethodAuthenticateStartResponse struct { unknownFields protoimpl.UnknownFields // The returned authentication URL - AuthUrl string `protobuf:"bytes,10,opt,name=auth_url,proto3" json:"auth_url,omitempty"` + AuthUrl string `protobuf:"bytes,10,opt,name=auth_url,proto3" json:"auth_url,omitempty" class:"public"` // @gotags: `class:"public"` // The returned token ID - TokenId string `protobuf:"bytes,30,opt,name=token_id,proto3" json:"token_id,omitempty"` + TokenId string `protobuf:"bytes,30,opt,name=token_id,proto3" json:"token_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *OidcAuthMethodAuthenticateStartResponse) Reset() { @@ -510,13 +510,13 @@ type OidcAuthMethodAuthenticateCallbackRequest struct { unknownFields protoimpl.UnknownFields // The returned code - Code string `protobuf:"bytes,10,opt,name=code,proto3" json:"code,omitempty"` + Code string `protobuf:"bytes,10,opt,name=code,proto3" json:"code,omitempty" class:"secret"` // @gotags: `class:"secret"` // The returned state - State string `protobuf:"bytes,20,opt,name=state,proto3" json:"state,omitempty"` + State string `protobuf:"bytes,20,opt,name=state,proto3" json:"state,omitempty" class:"public"` // @gotags: `class:"public"` // Error parameters, if they are returned - Error string `protobuf:"bytes,30,opt,name=error,proto3" json:"error,omitempty"` - ErrorDescription string `protobuf:"bytes,40,opt,name=error_description,proto3" json:"error_description,omitempty"` - ErrorUri string `protobuf:"bytes,50,opt,name=error_uri,proto3" json:"error_uri,omitempty"` + Error string `protobuf:"bytes,30,opt,name=error,proto3" json:"error,omitempty" class:"public"` // @gotags: `class:"public"` + ErrorDescription string `protobuf:"bytes,40,opt,name=error_description,proto3" json:"error_description,omitempty" class:"public"` // @gotags: `class:"public"` + ErrorUri string `protobuf:"bytes,50,opt,name=error_uri,proto3" json:"error_uri,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *OidcAuthMethodAuthenticateCallbackRequest) Reset() { @@ -593,7 +593,7 @@ type OidcAuthMethodAuthenticateCallbackResponse struct { unknownFields protoimpl.UnknownFields // The final redirection URL - FinalRedirectUrl string `protobuf:"bytes,10,opt,name=final_redirect_url,proto3" json:"final_redirect_url,omitempty"` + FinalRedirectUrl string `protobuf:"bytes,10,opt,name=final_redirect_url,proto3" json:"final_redirect_url,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *OidcAuthMethodAuthenticateCallbackResponse) Reset() { @@ -642,7 +642,7 @@ type OidcAuthMethodAuthenticateTokenRequest struct { unknownFields protoimpl.UnknownFields // The ID of the pending token - TokenId string `protobuf:"bytes,10,opt,name=token_id,proto3" json:"token_id,omitempty"` + TokenId string `protobuf:"bytes,10,opt,name=token_id,proto3" json:"token_id,omitempty" class:"private"` // @gotags: `class:"private"` } func (x *OidcAuthMethodAuthenticateTokenRequest) Reset() { @@ -693,7 +693,7 @@ type OidcAuthMethodAuthenticateTokenResponse struct { // The status. This will always be "unknown". It will never be forwarded to // the consumer. - Status string `protobuf:"bytes,10,opt,name=status,proto3" json:"status,omitempty"` + Status string `protobuf:"bytes,10,opt,name=status,proto3" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *OidcAuthMethodAuthenticateTokenResponse) Reset() { diff --git a/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable.go b/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable.go index 26c729eac3..97ec05b15a 100644 --- a/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable.go +++ b/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable.go @@ -45,6 +45,10 @@ func (req *AuthMethod) Tags() ([]encrypt.PointerTag, error) { Pointer: "/Attributes/Fields/signing_algorithms", Classification: encrypt.PublicClassification, }, + { + Pointer: "/Attributes/Fields/idp_ca_certs", + Classification: encrypt.PublicClassification, + }, { Pointer: "/Attributes/Fields/api_url_prefix", Classification: encrypt.PublicClassification, diff --git a/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable_test.go b/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable_test.go index 865deab882..a4018792aa 100644 --- a/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable_test.go +++ b/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/sdk/pbs/controller/api" "github.com/hashicorp/boundary/sdk/wrapper" "github.com/hashicorp/eventlogger" - "github.com/hashicorp/eventlogger/filters/encrypt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" @@ -19,11 +19,7 @@ func TestAuthMethod_Tags(t *testing.T) { ctx := context.Background() now := time.Now() wrapper := wrapper.TestWrapper(t) - testEncryptingFilter := &encrypt.Filter{ - Wrapper: wrapper, - HmacSalt: []byte("salt"), - HmacInfo: []byte("info"), - } + testEncryptingFilter := api.NewEncryptFilter(t, wrapper) tests := []struct { name string @@ -49,6 +45,7 @@ func TestAuthMethod_Tags(t *testing.T) { "client_secret_hmac": structpb.NewStringValue("public-client_secret_hmac"), "max_age": structpb.NewStringValue("public-max_age"), "signing_algorithms": structpb.NewStringValue("public-signing_algorithms"), + "idp_ca_certs": structpb.NewStringValue("public-signing_algorithms"), "api_url_prefix": structpb.NewStringValue("public-api_url_prefix"), "callback_url": structpb.NewStringValue("public-callback_url"), "allowed_audiences": structpb.NewStringValue("public-allowed_audiences"), @@ -87,6 +84,7 @@ func TestAuthMethod_Tags(t *testing.T) { "client_secret_hmac": structpb.NewStringValue("public-client_secret_hmac"), "max_age": structpb.NewStringValue("public-max_age"), "signing_algorithms": structpb.NewStringValue("public-signing_algorithms"), + "idp_ca_certs": structpb.NewStringValue("public-signing_algorithms"), "api_url_prefix": structpb.NewStringValue("public-api_url_prefix"), "callback_url": structpb.NewStringValue("public-callback_url"), "allowed_audiences": structpb.NewStringValue("public-allowed_audiences"), @@ -119,7 +117,7 @@ func TestAuthMethod_Tags(t *testing.T) { ScopeId: "scope-id", Name: &wrapperspb.StringValue{Value: "name"}, Description: &wrapperspb.StringValue{Value: "description"}, - Type: "oidc", + Type: "password", Attributes: &structpb.Struct{ Fields: map[string]*structpb.Value{ "min_login_name_length": structpb.NewStringValue("public-min_login_name_length"), @@ -145,7 +143,7 @@ func TestAuthMethod_Tags(t *testing.T) { ScopeId: "scope-id", Name: &wrapperspb.StringValue{Value: "name"}, Description: &wrapperspb.StringValue{Value: "description"}, - Type: "oidc", + Type: "password", Attributes: &structpb.Struct{ Fields: map[string]*structpb.Value{ "min_login_name_length": structpb.NewStringValue("public-min_login_name_length"), diff --git a/sdk/pbs/controller/api/testing.go b/sdk/pbs/controller/api/testing.go new file mode 100644 index 0000000000..0e771e924d --- /dev/null +++ b/sdk/pbs/controller/api/testing.go @@ -0,0 +1,23 @@ +package api + +import ( + "reflect" + "testing" + + "github.com/hashicorp/eventlogger/filters/encrypt" + wrapping "github.com/hashicorp/go-kms-wrapping" + "google.golang.org/protobuf/types/known/fieldmaskpb" +) + +// NewEncryptFilter is a copy of event.NewEncryptFilter since importing it would +// case circular deps. The primary reason for this test func is to make sure +// the proper IgnoreTypes are included for testing. +func NewEncryptFilter(t *testing.T, w wrapping.Wrapper) *encrypt.Filter { + t.Helper() + return &encrypt.Filter{ + Wrapper: w, + IgnoreTypes: []reflect.Type{ + reflect.TypeOf(&fieldmaskpb.FieldMask{}), + }, + } +}