From a679300b506fb843ffcbf70917e4cff1b350b54b Mon Sep 17 00:00:00 2001 From: Jim Date: Wed, 27 Oct 2021 09:37:14 -0400 Subject: [PATCH] feature (events): Classify auth method request/resp messages for audit events. (#1640) * refactor (oidc): Stop emitting error for not found token id Stop emitting errors when authtoken repo.IssueAuthToken is called and there's if no pending token. It's not an "normal" state and not an error condition. * feature (audit tagging): Add tags for auth method requests/responses Including testing functions for asserting audit events created when a service is called --- CONTRIBUTING.md | 1 + go.mod | 4 +- go.sum | 6 +- internal/adding-a-new-field-readme.md | 15 ++- internal/authtoken/repository.go | 2 +- .../api/services/auth_method_service.pb.go | 10 +- .../services/auth_method_service_taggable.go | 67 ++++++++++++ .../auth_method_service_taggable_test.go | 72 +++++++++++-- internal/observability/event/eventer.go | 21 +++- .../authmethods/v1/auth_method.proto | 20 ++-- .../api/services/v1/auth_method_service.proto | 10 +- .../api/authmethods/authenticate_test.go | 50 ++++++++- .../tests/api/authmethods/authmethod_test.go | 101 ++++++++++++++++-- internal/tests/api/testing.go | 80 ++++++++++++++ sdk/go.mod | 2 +- sdk/go.sum | 4 +- .../resources/authmethods/auth_method.pb.go | 20 ++-- .../authmethods/auth_method_taggable.go | 4 + .../authmethods/auth_method_taggable_test.go | 14 ++- sdk/pbs/controller/api/testing.go | 23 ++++ 20 files changed, 452 insertions(+), 74 deletions(-) create mode 100644 internal/tests/api/testing.go create mode 100644 sdk/pbs/controller/api/testing.go 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{}), + }, + } +}