diff --git a/CHANGELOG.md b/CHANGELOG.md index 28a28534d..bd52f521c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. a missing mandated cipher suite of the HTTP/2 (`h2`) specification that could result in no shared cipher suites between the Boundary API listener and those clients. ([PR](https://github.com/hashicorp/boundary/pull/1637)) +* vault: Fix credential store support when using Vault namespaces + ([Issue](https://github.com/hashicorp/boundary/issues/1597), + [PR](https://github.com/hashicorp/boundary/pull/1660)) ## 0.6.2 (2021/09/27) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9167495cc..5490871ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,6 +144,15 @@ $ make test-database-up $ make test ``` +By default the container name is `boundary-sql-tests`. +This can be changed in the same way as the port: + +``` +$ export TEST_CONTAINER_NAME="custom-name" +$ make test-database-up +$ docker logs custom-name +``` + The default docker image is built using the `postgres:11` base image. The image can be changed using a make option to test against other versions: diff --git a/Makefile b/Makefile index 9b060bfef..55b53210b 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,7 @@ protobuild: @protoc-go-inject-tag -input=./internal/kms/store/session_key.pb.go @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go @protoc-go-inject-tag -input=./internal/target/store/target.pb.go + @protoc-go-inject-tag -input=./internal/target/targettest/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/tcp/store/target.pb.go @protoc-go-inject-tag -input=./internal/auth/oidc/store/oidc.pb.go @protoc-go-inject-tag -input=./internal/scheduler/job/store/job.pb.go diff --git a/README.md b/README.md index 9cd43294e..f2685151e 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ for permanent installations, are detailed in our [Installation Guide](https://ww If you have the following requirements met locally: - Golang v1.16 or greater - Docker +- [Boundary UI Dependencies](https://github.com/hashicorp/boundary-ui#prerequisites) You can get up and running with Boundary quickly. Simply run: diff --git a/api/targets/option.gen.go b/api/targets/option.gen.go index 11f0f82ab..5c47abf6d 100644 --- a/api/targets/option.gen.go +++ b/api/targets/option.gen.go @@ -159,6 +159,18 @@ func DefaultDescription() Option { } } +func WithEgressCredentialSourceIds(inEgressCredentialSourceIds []string) Option { + return func(o *options) { + o.postMap["egress_credential_source_ids"] = inEgressCredentialSourceIds + } +} + +func DefaultEgressCredentialSourceIds() Option { + return func(o *options) { + o.postMap["egress_credential_source_ids"] = nil + } +} + func WithHostId(inHostId string) Option { return func(o *options) { o.postMap["host_id"] = inHostId diff --git a/api/targets/target.gen.go b/api/targets/target.gen.go index 0c9cca148..1e1a64fc7 100644 --- a/api/targets/target.gen.go +++ b/api/targets/target.gen.go @@ -33,6 +33,8 @@ type Target struct { ApplicationCredentialLibraries []*CredentialLibrary `json:"application_credential_libraries,omitempty"` ApplicationCredentialSourceIds []string `json:"application_credential_source_ids,omitempty"` ApplicationCredentialSources []*CredentialSource `json:"application_credential_sources,omitempty"` + EgressCredentialSourceIds []string `json:"egress_credential_source_ids,omitempty"` + EgressCredentialSources []*CredentialSource `json:"egress_credential_sources,omitempty"` Attributes map[string]interface{} `json:"attributes,omitempty"` AuthorizedActions []string `json:"authorized_actions,omitempty"` diff --git a/globals/fields.go b/globals/fields.go index 3ee466d03..1987041b3 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -64,4 +64,6 @@ const ( PluginNameField = "plugin_name" IpAddressesField = "ip_addresses" DnsNamesField = "dns_names" + EgressCredentialSourceIdsField = "egress_credential_source_ids" + EgressCredentialSourcesField = "egress_credential_sources" ) diff --git a/go.mod b/go.mod index a2e634bb1..a479cb43d 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,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.5 + github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 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 db0095859..dbad26584 100644 --- a/go.sum +++ b/go.sum @@ -478,8 +478,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.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/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/api/genapi/input.go b/internal/api/genapi/input.go index 46341352a..d06528493 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -670,6 +670,11 @@ var inputStructs = []*structInfo{ ProtoName: "application_credential_source_ids", FieldType: "[]string", }, + { + Name: "EgressCredentialSourceIds", + ProtoName: "egress_credential_source_ids", + FieldType: "[]string", + }, }, versionEnabled: true, typeOnCreate: true, diff --git a/internal/auth/oidc/auth_method.go b/internal/auth/oidc/auth_method.go index 1f2417424..de519a68f 100644 --- a/internal/auth/oidc/auth_method.go +++ b/internal/auth/oidc/auth_method.go @@ -2,10 +2,6 @@ package oidc import ( "context" - "crypto/ed25519" - "crypto/hmac" - "crypto/sha256" - "encoding/base64" "fmt" "net/url" "sort" @@ -13,7 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/auth/oidc/store" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" "github.com/hashicorp/boundary/internal/oplog" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/structwrapping" @@ -236,17 +232,14 @@ func (a *AuthMethod) hmacClientSecret(ctx context.Context, cipher wrapping.Wrapp if cipher == nil { return errors.New(ctx, errors.InvalidParameter, op, "missing cipher") } - reader, err := kms.NewDerivedReader(cipher, 32, []byte(a.PublicId), nil) + // this operation currently uses the legacy WithEd25519 option for hmac'ing. + // we should likely deprecate this and introduce a new "crypto version" of + // this attribute. + hm, err := crypto.HmacSha256(ctx, []byte(a.ClientSecret), cipher, []byte(a.PublicId), nil, crypto.WithBase64Encoding(), crypto.WithEd25519()) if err != nil { - return errors.Wrap(ctx, err, op) + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Code(errors.Encryption))) } - key, _, err := ed25519.GenerateKey(reader) - if err != nil { - return errors.New(ctx, errors.Encrypt, op, "unable to generate derived key") - } - mac := hmac.New(sha256.New, key) - _, _ = mac.Write([]byte(a.ClientSecret)) - a.ClientSecretHmac = base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) + a.ClientSecretHmac = hm return nil } diff --git a/internal/auth/oidc/service.go b/internal/auth/oidc/service.go index 568668a62..e3a074258 100644 --- a/internal/auth/oidc/service.go +++ b/internal/auth/oidc/service.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/wrappers/aead" @@ -189,7 +190,7 @@ func requestWrappingWrapper(ctx context.Context, k *kms.Kms, scopeId, authMethod } // okay, I guess we need to derive a new key for this combo of oidcWrapper and authMethod - reader, err := kms.NewDerivedReader(oidcWrapper, 32, []byte(authMethodId), []byte(scopeId)) + reader, err := crypto.NewDerivedReader(oidcWrapper, 32, []byte(authMethodId), []byte(scopeId)) if err != nil { return nil, errors.Wrap(ctx, err, op) } diff --git a/internal/cmd/commands/targetscmd/funcs.go b/internal/cmd/commands/targetscmd/funcs.go index 77cf8791f..8ffa0bb6c 100644 --- a/internal/cmd/commands/targetscmd/funcs.go +++ b/internal/cmd/commands/targetscmd/funcs.go @@ -33,6 +33,7 @@ type extraCmdVars struct { flagHostSources []string flagApplicationCredentialLibraries []string flagApplicationCredentialSources []string + flagEgressCredentialSources []string flagHostId string sar *targets.SessionAuthorizationResult } @@ -49,9 +50,9 @@ func extraActionsFlagsMapFuncImpl() map[string][]string { "add-credential-libraries": {"id", "application-credential-library", "version"}, "remove-credential-libraries": {"id", "application-credential-library", "version"}, "set-credential-libraries": {"id", "application-credential-library", "version"}, - "add-credential-sources": {"id", "application-credential-source", "version"}, - "remove-credential-sources": {"id", "application-credential-source", "version"}, - "set-credential-sources": {"id", "application-credential-source", "version"}, + "add-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, + "remove-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, + "set-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, } } @@ -361,6 +362,12 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) { Target: &c.flagApplicationCredentialSources, Usage: "The credential source for application purpose to add, set, or remove. May be specified multiple times.", }) + case "egress-credential-source": + f.StringSliceVar(&base.StringSliceVar{ + Name: "egress-credential-source", + Target: &c.flagEgressCredentialSources, + Usage: "The credential source for egress purpose to add, set, or remove. May be specified multiple times.", + }) } } @@ -460,7 +467,6 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op } case "add-credential-libraries", "remove-credential-libraries": - // TODO: As we add other purposes, add them to this check if len(c.flagApplicationCredentialLibraries) == 0 { c.UI.Error("No credential-libraries supplied via -application-credential-library") return false @@ -469,14 +475,19 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op case "add-credential-sources", "remove-credential-sources": // TODO: As we add other purposes, add them to this check - if len(c.flagApplicationCredentialSources) == 0 { - c.UI.Error("No credential sources supplied via -application-credential-source") + if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 { + c.UI.Error("No credential sources supplied via -application-credential-source or -egress-credential-source") return false } - *opts = append(*opts, targets.WithApplicationCredentialSourceIds(c.flagApplicationCredentialSources)) + + if len(c.flagApplicationCredentialSources) > 0 { + *opts = append(*opts, targets.WithApplicationCredentialSourceIds(c.flagApplicationCredentialSources)) + } + if len(c.flagEgressCredentialSources) > 0 { + *opts = append(*opts, targets.WithEgressCredentialSourceIds(c.flagEgressCredentialSources)) + } case "set-credential-libraries": - // TODO: As we add other purposes, add them to this check switch len(c.flagApplicationCredentialLibraries) { case 0: c.UI.Error("No credential-libraries supplied via -application-credential-library") @@ -493,10 +504,14 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op case "set-credential-sources": // TODO: As we add other purposes, add them to this check + if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 { + c.UI.Error("No credential sources supplied via -application-credential-source or -egress-credential-source") + return false + } + switch len(c.flagApplicationCredentialSources) { case 0: - c.UI.Error("No credential sources supplied via -application-credential-source") - return false + // do nothing case 1: if c.flagApplicationCredentialSources[0] == "null" { *opts = append(*opts, targets.DefaultApplicationCredentialSourceIds()) @@ -506,6 +521,18 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op default: *opts = append(*opts, targets.WithApplicationCredentialSourceIds(c.flagApplicationCredentialSources)) } + switch len(c.flagEgressCredentialSources) { + case 0: + // do nothing + case 1: + if c.flagEgressCredentialSources[0] == "null" { + *opts = append(*opts, targets.DefaultEgressCredentialSourceIds()) + break + } + fallthrough + default: + *opts = append(*opts, targets.WithEgressCredentialSourceIds(c.flagEgressCredentialSources)) + } case "authorize-session": if len(c.flagHostId) != 0 { diff --git a/internal/credential/credential.go b/internal/credential/credential.go index 98b311637..5c337ae21 100644 --- a/internal/credential/credential.go +++ b/internal/credential/credential.go @@ -28,6 +28,10 @@ type Library interface { // Purpose is the purpose of the credential. type Purpose string +func (p Purpose) String() string { + return string(p) +} + // Credential purpose values. const ( // ApplicationPurpose is a credential used for application specific @@ -45,6 +49,13 @@ const ( EgressPurpose Purpose = "egress" ) +// ValidPurposes are the set of all credential Purposes. +var ValidPurposes = []Purpose{ + ApplicationPurpose, + IngressPurpose, + EgressPurpose, +} + // SecretData represents secret data. type SecretData interface{} diff --git a/internal/credential/vault/client_certificate.go b/internal/credential/vault/client_certificate.go index b368c7d72..345f97c80 100644 --- a/internal/credential/vault/client_certificate.go +++ b/internal/credential/vault/client_certificate.go @@ -2,15 +2,12 @@ package vault import ( "context" - "crypto/ed25519" - "crypto/hmac" - "crypto/sha256" "database/sql" "github.com/hashicorp/boundary/internal/credential/vault/store" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" "github.com/hashicorp/boundary/internal/oplog" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/structwrapping" @@ -103,17 +100,14 @@ func (c *ClientCertificate) hmacCertificateKey(ctx context.Context, cipher wrapp if cipher == nil { return errors.New(ctx, errors.InvalidParameter, op, "missing cipher") } - reader, err := kms.NewDerivedReader(cipher, 32, []byte(c.StoreId), nil) + // this operation currently uses the legacy WithEd25519 option for hmac'ing. + // we should likely deprecate this and introduce a new "crypto version" of + // this attribute. + hm, err := crypto.HmacSha256(ctx, c.CertificateKey, cipher, []byte(c.StoreId), nil, crypto.WithEd25519()) if err != nil { return errors.Wrap(ctx, err, op) } - key, _, err := ed25519.GenerateKey(reader) - if err != nil { - return errors.New(ctx, errors.Encrypt, op, "unable to generate derived key") - } - mac := hmac.New(sha256.New, key) - _, _ = mac.Write(c.CertificateKey) - c.CertificateKeyHmac = mac.Sum(nil) + c.CertificateKeyHmac = []byte(hm) return nil } diff --git a/internal/credential/vault/vault.go b/internal/credential/vault/vault.go index 9f29c6f95..0e2cc1add 100644 --- a/internal/credential/vault/vault.go +++ b/internal/credential/vault/vault.go @@ -79,6 +79,10 @@ func newClient(c *clientConfig) (*client, error) { } vClient.SetToken(string(c.Token)) + if c.Namespace != "" { + vClient.SetNamespace(c.Namespace) + } + return &client{ cl: vClient, token: c.Token, diff --git a/internal/credential/vault/vault_token.go b/internal/credential/vault/vault_token.go index 5e7321f65..c653ec751 100644 --- a/internal/credential/vault/vault_token.go +++ b/internal/credential/vault/vault_token.go @@ -2,18 +2,16 @@ package vault import ( "context" - "crypto/hmac" - "crypto/sha256" "database/sql" "time" "github.com/hashicorp/boundary/internal/credential/vault/store" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/libs/crypto" "github.com/hashicorp/boundary/internal/oplog" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/structwrapping" - "golang.org/x/crypto/blake2b" "google.golang.org/protobuf/proto" ) @@ -74,16 +72,15 @@ func newToken(storeId string, token TokenSecret, accessor []byte, expiration tim accessorCopy := make([]byte, len(accessor)) copy(accessorCopy, accessor) - key := blake2b.Sum256(accessorCopy) - mac := hmac.New(sha256.New, key[:]) - _, _ = mac.Write(tokenCopy) - hmac := mac.Sum(nil) - + hmac, err := crypto.HmacSha256WithPrk(context.Background(), tokenCopy, accessorCopy) + if err != nil { + return nil, errors.WrapDeprecated(err, op, errors.WithCode(errors.Encrypt)) + } t := &Token{ expiration: expiration.Round(time.Second), Token: &store.Token{ StoreId: storeId, - TokenHmac: hmac, + TokenHmac: []byte(hmac), Token: tokenCopy, Status: string(CurrentToken), }, diff --git a/internal/db/common/update.go b/internal/db/common/update.go index 71b11e3fd..2a76a6f59 100644 --- a/internal/db/common/update.go +++ b/internal/db/common/update.go @@ -46,6 +46,16 @@ func UpdateFields(i interface{}, fieldMaskPaths []string, setToNullPaths []strin val := reflect.Indirect(reflect.ValueOf(i)) structTyp := val.Type() for i := 0; i < structTyp.NumField(); i++ { + if f, ok := maskPaths[strings.ToUpper(structTyp.Field(i).Name)]; ok { + updateFields[f] = val.Field(i).Interface() + found[strings.ToUpper(f)] = struct{}{} + continue + } + if f, ok := nullPaths[strings.ToUpper(structTyp.Field(i).Name)]; ok { + updateFields[f] = gorm.Expr("NULL") + found[strings.ToUpper(f)] = struct{}{} + continue + } kind := structTyp.Field(i).Type.Kind() if kind == reflect.Struct || kind == reflect.Ptr { embType := structTyp.Field(i).Type @@ -76,14 +86,6 @@ func UpdateFields(i interface{}, fieldMaskPaths []string, setToNullPaths []strin continue } } - if f, ok := maskPaths[strings.ToUpper(structTyp.Field(i).Name)]; ok { - updateFields[f] = val.Field(i).Interface() - found[strings.ToUpper(f)] = struct{}{} - } - if f, ok := nullPaths[strings.ToUpper(structTyp.Field(i).Name)]; ok { - updateFields[f] = gorm.Expr("NULL") - found[strings.ToUpper(f)] = struct{}{} - } } if missing := findMissingPaths(setToNullPaths, found); len(missing) != 0 { diff --git a/internal/db/common/update_test.go b/internal/db/common/update_test.go index 4da3a16c6..22893f06e 100644 --- a/internal/db/common/update_test.go +++ b/internal/db/common/update_test.go @@ -4,9 +4,13 @@ import ( "testing" "github.com/hashicorp/boundary/internal/db/db_test" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/gorm" ) @@ -398,6 +402,20 @@ func TestUpdateFields(t *testing.T) { assert.Equal(tt.want, got) }) } + t.Run("valid-embedded-timestamp", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + wantTs := ×tamp.Timestamp{ + Timestamp: ×tamppb.Timestamp{ + Seconds: 1, + Nanos: 1, + }, + } + u := testUser(t, "", "") + u.UpdateTime = wantTs + got, err := UpdateFields(u, []string{"UpdateTime"}, nil) + require.NoError(err) + assert.True(proto.Equal(wantTs, got["UpdateTime"].(*timestamp.Timestamp))) + }) } func testUser(t *testing.T, name, email string) *db_test.TestUser { diff --git a/internal/db/db_test/db.go b/internal/db/db_test/db.go index 0d641b2a1..12218629a 100644 --- a/internal/db/db_test/db.go +++ b/internal/db/db_test/db.go @@ -2,15 +2,19 @@ package db_test import ( + "errors" + "github.com/hashicorp/go-secure-stdlib/base62" "google.golang.org/protobuf/proto" ) const ( - defaultUserTablename = "db_test_user" - defaultCarTableName = "db_test_car" - defaultRentalTableName = "db_test_rental" - defaultScooterTableName = "db_test_scooter" + defaultUserTablename = "db_test_user" + defaultCarTableName = "db_test_car" + defaultRentalTableName = "db_test_rental" + defaultScooterTableName = "db_test_scooter" + defaultAccessoryTableName = "db_test_accessory" + defaultScooterAccessoryTableName = "db_test_scooter_accessory" ) type TestUser struct { @@ -153,6 +157,71 @@ func (t *TestScooter) SetTableName(name string) { t.table = name } +type TestAccessory struct { + *StoreTestAccessory + table string `gorm:"-"` +} + +func NewTestAccessory(description string) (*TestAccessory, error) { + if description == "" { + return nil, errors.New("missing description") + } + return &TestAccessory{StoreTestAccessory: &StoreTestAccessory{Description: description}}, nil +} + +func (t *TestAccessory) Clone() interface{} { + s := proto.Clone(t.StoreTestAccessory) + return &TestAccessory{ + StoreTestAccessory: s.(*StoreTestAccessory), + } +} + +func (t *TestAccessory) TableName() string { + if t.table != "" { + return t.table + } + return defaultAccessoryTableName +} + +func (t *TestAccessory) SetTableName(name string) { + t.table = name +} + +type TestScooterAccessory struct { + *StoreTestScooterAccessory + table string `gorm:"-"` +} + +func NewTestScooterAccessory(scooterId, accessoryId uint32) (*TestScooterAccessory, error) { + if accessoryId == 0 { + return nil, errors.New("mssing accessory id") + } + return &TestScooterAccessory{ + StoreTestScooterAccessory: &StoreTestScooterAccessory{ + ScooterId: scooterId, + AccessoryId: accessoryId, + }, + }, nil +} + +func (t *TestScooterAccessory) Clone() interface{} { + s := proto.Clone(t.StoreTestScooterAccessory) + return &TestScooterAccessory{ + StoreTestScooterAccessory: s.(*StoreTestScooterAccessory), + } +} + +func (t *TestScooterAccessory) TableName() string { + if t.table != "" { + return t.table + } + return defaultScooterAccessoryTableName +} + +func (t *TestScooterAccessory) SetTableName(name string) { + t.table = name +} + type Cloner interface { Clone() interface{} } diff --git a/internal/db/db_test/db_test.pb.go b/internal/db/db_test/db_test.pb.go index b097025ad..5a0d95897 100644 --- a/internal/db/db_test/db_test.pb.go +++ b/internal/db/db_test/db_test.pb.go @@ -453,6 +453,133 @@ func (x *StoreTestScooter) GetMpg() int32 { return 0 } +// StoreTestAccessory used in the db tests only and provides a gorm resource with +// an id that's not a private or public id +type StoreTestAccessory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: gorm:"primary_key" + AccessoryId uint32 `protobuf:"varint,1,opt,name=accessory_id,json=accessoryId,proto3" json:"accessory_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"default:not_null"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty" gorm:"default:not_null"` +} + +func (x *StoreTestAccessory) Reset() { + *x = StoreTestAccessory{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreTestAccessory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreTestAccessory) ProtoMessage() {} + +func (x *StoreTestAccessory) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreTestAccessory.ProtoReflect.Descriptor instead. +func (*StoreTestAccessory) Descriptor() ([]byte, []int) { + return file_controller_storage_db_db_test_v1_db_test_proto_rawDescGZIP(), []int{4} +} + +func (x *StoreTestAccessory) GetAccessoryId() uint32 { + if x != nil { + return x.AccessoryId + } + return 0 +} + +func (x *StoreTestAccessory) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +// StoreTestScooterAccessory used in the db tests only and provides a gorm +// resource with multiple pks +type StoreTestScooterAccessory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: gorm:"primary_key" + AccessoryId uint32 `protobuf:"varint,1,opt,name=accessory_id,json=accessoryId,proto3" json:"accessory_id,omitempty" gorm:"primary_key"` + // @inject_tag: gorm:"primary_key" + ScooterId uint32 `protobuf:"varint,2,opt,name=scooter_id,json=scooterId,proto3" json:"scooter_id,omitempty" gorm:"primary_key"` + // @inject_tag: `gorm:"default:null"` + Review string `protobuf:"bytes,3,opt,name=review,proto3" json:"review,omitempty" gorm:"default:null"` +} + +func (x *StoreTestScooterAccessory) Reset() { + *x = StoreTestScooterAccessory{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreTestScooterAccessory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreTestScooterAccessory) ProtoMessage() {} + +func (x *StoreTestScooterAccessory) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreTestScooterAccessory.ProtoReflect.Descriptor instead. +func (*StoreTestScooterAccessory) Descriptor() ([]byte, []int) { + return file_controller_storage_db_db_test_v1_db_test_proto_rawDescGZIP(), []int{5} +} + +func (x *StoreTestScooterAccessory) GetAccessoryId() uint32 { + if x != nil { + return x.AccessoryId + } + return 0 +} + +func (x *StoreTestScooterAccessory) GetScooterId() uint32 { + if x != nil { + return x.ScooterId + } + return 0 +} + +func (x *StoreTestScooterAccessory) GetReview() string { + if x != nil { + return x.Review + } + return "" +} + var File_controller_storage_db_db_test_v1_db_test_proto protoreflect.FileDescriptor var file_controller_storage_db_db_test_v1_db_test_proto_rawDesc = []byte{ @@ -535,12 +662,25 @@ var file_controller_storage_db_db_test_v1_db_test_proto_rawDesc = []byte{ 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x70, - 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x70, 0x67, 0x42, 0x3b, 0x5a, 0x39, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x62, 0x2f, 0x64, 0x62, 0x5f, 0x74, 0x65, 0x73, - 0x74, 0x3b, 0x64, 0x62, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x70, 0x67, 0x22, 0x59, 0x0a, 0x12, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, + 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x79, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x75, 0x0a, 0x19, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x54, 0x65, 0x73, 0x74, 0x53, 0x63, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x6f, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x63, 0x6f, + 0x6f, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x42, 0x3b, + 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x62, 0x2f, 0x64, 0x62, 0x5f, 0x74, + 0x65, 0x73, 0x74, 0x3b, 0x64, 0x62, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -555,23 +695,25 @@ func file_controller_storage_db_db_test_v1_db_test_proto_rawDescGZIP() []byte { return file_controller_storage_db_db_test_v1_db_test_proto_rawDescData } -var file_controller_storage_db_db_test_v1_db_test_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_db_db_test_v1_db_test_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_controller_storage_db_db_test_v1_db_test_proto_goTypes = []interface{}{ - (*StoreTestUser)(nil), // 0: controller.storage.db.db_test.v1.StoreTestUser - (*StoreTestCar)(nil), // 1: controller.storage.db.db_test.v1.StoreTestCar - (*StoreTestRental)(nil), // 2: controller.storage.db.db_test.v1.StoreTestRental - (*StoreTestScooter)(nil), // 3: controller.storage.db.db_test.v1.StoreTestScooter - (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp + (*StoreTestUser)(nil), // 0: controller.storage.db.db_test.v1.StoreTestUser + (*StoreTestCar)(nil), // 1: controller.storage.db.db_test.v1.StoreTestCar + (*StoreTestRental)(nil), // 2: controller.storage.db.db_test.v1.StoreTestRental + (*StoreTestScooter)(nil), // 3: controller.storage.db.db_test.v1.StoreTestScooter + (*StoreTestAccessory)(nil), // 4: controller.storage.db.db_test.v1.StoreTestAccessory + (*StoreTestScooterAccessory)(nil), // 5: controller.storage.db.db_test.v1.StoreTestScooterAccessory + (*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_db_db_test_v1_db_test_proto_depIdxs = []int32{ - 4, // 0: controller.storage.db.db_test.v1.StoreTestUser.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 1: controller.storage.db.db_test.v1.StoreTestUser.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 2: controller.storage.db.db_test.v1.StoreTestCar.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 3: controller.storage.db.db_test.v1.StoreTestCar.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 4: controller.storage.db.db_test.v1.StoreTestRental.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 5: controller.storage.db.db_test.v1.StoreTestRental.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 6: controller.storage.db.db_test.v1.StoreTestScooter.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 7: controller.storage.db.db_test.v1.StoreTestScooter.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 0: controller.storage.db.db_test.v1.StoreTestUser.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 1: controller.storage.db.db_test.v1.StoreTestUser.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 2: controller.storage.db.db_test.v1.StoreTestCar.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 3: controller.storage.db.db_test.v1.StoreTestCar.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 4: controller.storage.db.db_test.v1.StoreTestRental.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 5: controller.storage.db.db_test.v1.StoreTestRental.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 6: controller.storage.db.db_test.v1.StoreTestScooter.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 7: controller.storage.db.db_test.v1.StoreTestScooter.update_time:type_name -> controller.storage.timestamp.v1.Timestamp 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name @@ -633,6 +775,30 @@ func file_controller_storage_db_db_test_v1_db_test_proto_init() { return nil } } + file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreTestAccessory); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_db_db_test_v1_db_test_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreTestScooterAccessory); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -640,7 +806,7 @@ func file_controller_storage_db_db_test_v1_db_test_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_db_db_test_v1_db_test_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 749d5eb85..8287b2c5a 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -43,9 +43,11 @@ const ( // Reader interface defines lookups/searching for resources type Reader interface { // LookupById will lookup a resource by its primary key id, which must be - // unique. The resourceWithIder must implement either ResourcePublicIder or - // ResourcePrivateIder interface. - LookupById(ctx context.Context, resourceWithIder interface{}, opt ...Option) error + // unique. If the resource implements either ResourcePublicIder or + // ResourcePrivateIder interface, then they are used as the resource's + // primary key for lookup. Otherwise, the resource tags are used to + // determine it's primary key(s) for lookup. + LookupById(ctx context.Context, resource interface{}, opt ...Option) error // LookupByPublicId will lookup resource by its public_id which must be unique. LookupByPublicId(ctx context.Context, resource ResourcePublicIder, opt ...Option) error @@ -1048,11 +1050,11 @@ func (rw *Db) LookupById(ctx context.Context, resourceWithIder interface{}, _ .. if reflect.ValueOf(resourceWithIder).Kind() != reflect.Ptr { return errors.New(ctx, errors.InvalidParameter, op, "interface parameter must to be a pointer") } - primaryKey, where, err := primaryKeyWhere(ctx, resourceWithIder) + where, keys, err := rw.primaryKeysWhere(ctx, resourceWithIder) if err != nil { return errors.Wrap(ctx, err, op) } - if err := rw.underlying.Where(where, primaryKey).First(resourceWithIder).Error; err != nil { + if err := rw.underlying.Where(where, keys...).First(resourceWithIder).Error; err != nil { if err == gorm.ErrRecordNotFound { return errors.E(ctx, errors.WithCode(errors.RecordNotFound), errors.WithOp(op), errors.WithoutEvent()) } @@ -1061,23 +1063,48 @@ func (rw *Db) LookupById(ctx context.Context, resourceWithIder interface{}, _ .. return nil } -func primaryKeyWhere(ctx context.Context, resourceWithIder interface{}) (pkey string, w string, e error) { - const op = "db.primaryKeyWhere" - var primaryKey, where string - switch resourceType := resourceWithIder.(type) { +func (rw *Db) primaryKeysWhere(ctx context.Context, i interface{}) (string, []interface{}, error) { + const op = "db.primaryKeysWhere" + var fieldNames []string + var fieldValues []interface{} + tx := rw.underlying.Model(i) + if err := tx.Statement.Parse(i); err != nil { + return "", nil, errors.Wrap(ctx, err, op, errors.WithoutEvent()) + } + switch resourceType := i.(type) { case ResourcePublicIder: - primaryKey = resourceType.GetPublicId() - where = "public_id = ?" + if resourceType.GetPublicId() == "" { + return "", nil, errors.New(ctx, errors.InvalidParameter, op, "missing primary key", errors.WithoutEvent()) + } + fieldValues = []interface{}{resourceType.GetPublicId()} + fieldNames = []string{"public_id"} case ResourcePrivateIder: - primaryKey = resourceType.GetPrivateId() - where = "private_id = ?" + if resourceType.GetPrivateId() == "" { + return "", nil, errors.New(ctx, errors.InvalidParameter, op, "missing primary key", errors.WithoutEvent()) + } + fieldValues = []interface{}{resourceType.GetPrivateId()} + fieldNames = []string{"private_id"} default: - return "", "", errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported interface type %T", resourceWithIder), errors.WithoutEvent()) + v := reflect.ValueOf(i) + for _, f := range tx.Statement.Schema.PrimaryFields { + if f.PrimaryKey { + val, isZero := f.ValueOf(v) + if isZero { + return "", nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("primary field %s is zero", f.Name)) + } + fieldNames = append(fieldNames, f.DBName) + fieldValues = append(fieldValues, val) + } + } } - if primaryKey == "" { - return "", "", errors.New(ctx, errors.InvalidParameter, op, "missing primary key", errors.WithoutEvent()) + if len(fieldNames) == 0 { + return "", nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("no primary key(s) for %t", i)) } - return primaryKey, where, nil + clauses := make([]string, 0, len(fieldNames)) + for _, col := range fieldNames { + clauses = append(clauses, fmt.Sprintf("%s = ?", col)) + } + return strings.Join(clauses, " and "), fieldValues, nil } // LookupByPublicId will lookup resource by its public_id, which must be unique. diff --git a/internal/db/read_writer_test.go b/internal/db/read_writer_test.go index 793663dd4..86eac489a 100644 --- a/internal/db/read_writer_test.go +++ b/internal/db/read_writer_test.go @@ -571,6 +571,17 @@ func TestDb_Update(t *testing.T) { assert.Equal(0, rowsUpdated) assert.Contains(err.Error(), "db.Update: oplog validation failed: db.validateOplogArgs: missing metadata: parameter violation: error #100") }) + t.Run("multi-column", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + w := Db{underlying: db} + scooter := testScooter(t, db, "", 0) + accessory := testAccessory(t, db, "test accessory") + scooterAccessory := testScooterAccessory(t, db, scooter.Id, accessory.AccessoryId) + scooterAccessory.Review = "this is great" + rowsUpdated, err := w.Update(context.Background(), scooterAccessory, []string{"Review"}, nil) + require.NoError(err) + assert.Equal(1, rowsUpdated) + }) } // testUserWithVet gives us a model that implements VetForWrite() without any @@ -826,7 +837,7 @@ func TestDb_LookupByPublicId(t *testing.T) { require.NoError(err) err = w.LookupByPublicId(context.Background(), foundUser) require.Error(err) - assert.Contains(err.Error(), "db.LookupById: db.primaryKeyWhere: missing primary key: parameter violation: error #100") + assert.Contains(err.Error(), "db.LookupById: db.primaryKeysWhere: missing primary key: parameter violation: error #100") }) t.Run("not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) @@ -1448,6 +1459,16 @@ func TestDb_Delete(t *testing.T) { err = TestVerifyOplog(t, &w, user.PublicId, WithOperation(oplog.OpType_OP_TYPE_UNSPECIFIED), WithCreateNotBefore(10*time.Second)) require.NoError(err) }) + t.Run("multi-column", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + w := Db{underlying: db} + scooter := testScooter(t, db, "", 0) + accessory := testAccessory(t, db, "test accessory") + scooterAccessory := testScooterAccessory(t, db, scooter.Id, accessory.AccessoryId) + rowsDeleted, err := w.Delete(context.Background(), scooterAccessory) + require.NoError(err) + assert.Equal(1, rowsDeleted) + }) } } @@ -1983,14 +2004,41 @@ func testScooter(t *testing.T, conn *DB, model string, mpg int32) *db_test.TestS return u } +func testAccessory(t *testing.T, conn *DB, description string) *db_test.TestAccessory { + t.Helper() + require := require.New(t) + a, err := db_test.NewTestAccessory(description) + require.NoError(err) + if conn != nil { + err = conn.Create(a).Error + require.NoError(err) + } + return a +} + +func testScooterAccessory(t *testing.T, conn *DB, scooterId, accessoryId uint32) *db_test.TestScooterAccessory { + t.Helper() + require := require.New(t) + a, err := db_test.NewTestScooterAccessory(scooterId, accessoryId) + require.NoError(err) + if conn != nil { + err = conn.Create(a).Error + require.NoError(err) + } + return a +} + func TestDb_LookupById(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") scooter := testScooter(t, db, "", 0) user := testUser(t, db, "", "", "") + accessory := testAccessory(t, db, "test accessory") + scooterAccessory := testScooterAccessory(t, db, scooter.Id, accessory.AccessoryId) + type args struct { - resourceWithIder interface{} - opt []Option + resource interface{} + opt []Option } tests := []struct { name string @@ -2004,7 +2052,7 @@ func TestDb_LookupById(t *testing.T) { name: "simple-private-id", underlying: db, args: args{ - resourceWithIder: scooter, + resource: scooter, }, wantErr: false, want: scooter, @@ -2013,16 +2061,47 @@ func TestDb_LookupById(t *testing.T) { name: "simple-public-id", underlying: db, args: args{ - resourceWithIder: user, + resource: user, }, wantErr: false, want: user, }, + { + name: "simple-non-public-non-private-id", + underlying: db, + args: args{ + resource: accessory, + }, + wantErr: false, + want: accessory, + }, + { + name: "compond", + underlying: db, + args: args{ + resource: scooterAccessory, + }, + wantErr: false, + want: scooterAccessory, + }, + { + name: "compond-with-zero-value-pk", + underlying: db, + args: args{ + resource: func() interface{} { + cp := scooterAccessory.Clone() + cp.(*db_test.TestScooterAccessory).ScooterId = 0 + return cp + }(), + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, { name: "missing-public-id", underlying: db, args: args{ - resourceWithIder: &db_test.TestUser{ + resource: &db_test.TestUser{ StoreTestUser: &db_test.StoreTestUser{}, }, }, @@ -2033,7 +2112,7 @@ func TestDb_LookupById(t *testing.T) { name: "missing-private-id", underlying: db, args: args{ - resourceWithIder: &db_test.TestScooter{ + resource: &db_test.TestScooter{ StoreTestScooter: &db_test.StoreTestScooter{}, }, }, @@ -2044,7 +2123,7 @@ func TestDb_LookupById(t *testing.T) { name: "not-an-ider", underlying: db, args: args{ - resourceWithIder: &db_test.NotIder{}, + resource: &db_test.NotIder{}, }, wantErr: true, wantIsErr: errors.InvalidParameter, @@ -2053,7 +2132,7 @@ func TestDb_LookupById(t *testing.T) { name: "missing-underlying-db", underlying: nil, args: args{ - resourceWithIder: user, + resource: user, }, wantErr: true, wantIsErr: errors.InvalidParameter, @@ -2065,13 +2144,13 @@ func TestDb_LookupById(t *testing.T) { rw := &Db{ underlying: tt.underlying, } - cloner, ok := tt.args.resourceWithIder.(db_test.Cloner) + cloner, ok := tt.args.resource.(db_test.Cloner) require.True(ok) cp := cloner.Clone() err := rw.LookupById(context.Background(), cp, tt.args.opt...) if tt.wantErr { require.Error(err) - require.True(errors.Match(errors.T(tt.wantIsErr), err)) + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err code: %q got: %q", tt.wantIsErr, err) return } require.NoError(err) diff --git a/internal/db/schema/migrations/oss/postgres/19/01_db.up.sql b/internal/db/schema/migrations/oss/postgres/19/01_db.up.sql new file mode 100644 index 000000000..352e80934 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/19/01_db.up.sql @@ -0,0 +1,56 @@ +begin; + +create table if not exists db_test_accessory ( + accessory_id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + description text not null +); + +create trigger + update_time_column +before +update on db_test_accessory + for each row execute procedure update_time_column(); + +create trigger + immutable_columns +before +update on db_test_accessory + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_accessory + for each row execute procedure default_create_time(); + + +create table if not exists db_test_scooter_accessory ( + accessory_id bigint references db_test_accessory(accessory_id), + scooter_id bigint references db_test_scooter(id), + create_time wt_timestamp, + update_time wt_timestamp, + review text, + primary key(accessory_id, scooter_id) +); + +create trigger + update_time_column +before +update on db_test_scooter_accessory + for each row execute procedure update_time_column(); + +create trigger + immutable_columns +before +update on db_test_scooter_accessory + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_scooter_accessory + for each row execute procedure default_create_time(); + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres_16_05_test.go b/internal/db/schema/migrations/oss/postgres_16_05_test.go index d2c279751..a6c4bc081 100644 --- a/internal/db/schema/migrations/oss/postgres_16_05_test.go +++ b/internal/db/schema/migrations/oss/postgres_16_05_test.go @@ -165,16 +165,19 @@ func testSessionCredentialParams(t *testing.T, conn *db.DB, kms *kms.Kms, wrappe ctx := context.Background() stores := vault.TestCredentialStores(t, conn, wrapper, tar.GetScopeId(), 1) - libs := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) + libIds := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) + libs := []*target.CredentialLibrary{ + target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[1].GetPublicId(), credential.ApplicationPurpose), + } targetRepo, err := target.NewRepository(rw, rw, kms) require.NoError(t, err) - _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), []string{libs[0].PublicId, libs[1].PublicId}) + _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), libs) require.NoError(t, err) creds := []*session.DynamicCredential{ - session.NewDynamicCredential(libs[0].GetPublicId(), credential.ApplicationPurpose), - session.NewDynamicCredential(libs[0].GetPublicId(), credential.IngressPurpose), - session.NewDynamicCredential(libs[1].GetPublicId(), credential.EgressPurpose), + session.NewDynamicCredential(libIds[0].GetPublicId(), credential.ApplicationPurpose), + session.NewDynamicCredential(libIds[1].GetPublicId(), credential.ApplicationPurpose), } return creds } diff --git a/internal/db/sqltest/Makefile b/internal/db/sqltest/Makefile index 04731bb21..a05dadd79 100644 --- a/internal/db/sqltest/Makefile +++ b/internal/db/sqltest/Makefile @@ -21,6 +21,8 @@ POSTGRES_DOCKER_IMAGE := $(POSTGRES_DOCKER_IMAGE_BASE):$(PG_DOCKER_TAG) PG_TAP_DOCKER_IMAGE_BASE ?= subzerocloud/pgtap PG_TAP_DOCKER_IMAGE := $(PG_TAP_DOCKER_IMAGE_BASE):$(PG_TAP_DOCKER_TAG) +SQL_TEST_CONTAINER_NAME ?= boundary-sql-tests + # re-write paths for docker dockerized_tests = $(patsubst tests/%,/test/%,$(TESTS)) @@ -29,7 +31,7 @@ test: @echo Using $(POSTGRES_DOCKER_IMAGE) @echo Using $(PG_TAP_DOCKER_IMAGE) @docker run -d \ - --name boundary-sql-tests \ + --name $(SQL_TEST_CONTAINER_NAME) \ -e POSTGRES_PASSWORD=boundary \ -e POSTGRES_USER=boundary \ -e POSTGRES_DB=boundary \ @@ -38,7 +40,7 @@ test: $(POSTGRES_DOCKER_IMAGE) @docker run -it --rm \ --name test \ - --link boundary-sql-tests:db \ + --link $(SQL_TEST_CONTAINER_NAME):db \ -e DATABASE=boundary \ -e HOST=db \ -e PORT=5432 \ @@ -47,12 +49,12 @@ test: -e TESTS="$(PROVE_OPTS) $(dockerized_tests)" \ -v "$(CWD)/tests":/test \ $(PG_TAP_DOCKER_IMAGE); \ - (ret=$$?; docker stop boundary-sql-tests &>/dev/null && docker rm -v boundary-sql-tests &>/dev/null && exit $$ret) + (ret=$$?; docker stop $(SQL_TEST_CONTAINER_NAME) &>/dev/null && docker rm -v $(SQL_TEST_CONTAINER_NAME) &>/dev/null && exit $$ret) database-up: @echo Using $(POSTGRES_DOCKER_IMAGE) @docker run -d \ - --name boundary-sql-tests \ + --name $(SQL_TEST_CONTAINER_NAME) \ -e POSTGRES_PASSWORD=boundary \ -e POSTGRES_USER=boundary \ -e POSTGRES_DB=boundary \ @@ -64,7 +66,7 @@ run-tests: @echo Using $(PG_TAP_DOCKER_IMAGE) @docker run -it --rm \ --name test \ - --link boundary-sql-tests:db \ + --link $(SQL_TEST_CONTAINER_NAME):db \ -e DATABASE=boundary \ -e HOST=db \ -e PORT=5432 \ @@ -75,7 +77,7 @@ run-tests: $(PG_TAP_DOCKER_IMAGE) clean: - docker stop boundary-sql-tests || true - docker rm -v boundary-sql-tests || true + docker stop $(SQL_TEST_CONTAINER_NAME) || true + docker rm -v $(SQL_TEST_CONTAINER_NAME) || true .PHONY: all clean test database-up run-tests diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 81d63567f..441143e83 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -2819,6 +2819,12 @@ "items": { "type": "string" } + }, + "egress_credential_source_ids": { + "type": "array", + "items": { + "type": "string" + } } } } @@ -3055,6 +3061,12 @@ "items": { "type": "string" } + }, + "egress_credential_source_ids": { + "type": "array", + "items": { + "type": "string" + } } } } @@ -3240,9 +3252,15 @@ "items": { "type": "string" } + }, + "egress_credential_source_ids": { + "type": "array", + "items": { + "type": "string" + } } }, - "description": "Sets the values for application_credential_source_ids. If any\ncredential_source_id field that is not set in the request will result in\nthose fields being cleared." + "description": "Sets the values for credential sources. Any credential_source_id field that\nis not set in the request will result in those fields being cleared." } } ], @@ -5127,6 +5145,22 @@ "description": "Output only. The application credential sources associated with this Target.", "readOnly": true }, + "egress_credential_source_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Output only. The IDs of the egress credential source ids associated with this Target.", + "readOnly": true + }, + "egress_credential_sources": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.api.resources.targets.v1.CredentialSource" + }, + "description": "Output only. The egress credential sources associated with this Target.", + "readOnly": true + }, "attributes": { "type": "object", "description": "The attributes that are applicable for the specific Target." 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 0dc6fb6b6..ad9176bcd 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 @@ -10,6 +10,7 @@ import ( "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" @@ -81,7 +82,7 @@ func TestAuthenticate_Tags(t *testing.T) { "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(""), + "token": structpb.NewStringValue(encrypt.RedactedData), }, }, }, @@ -121,8 +122,8 @@ func TestAuthenticate_Tags(t *testing.T) { "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(""), + "password": structpb.NewStringValue(encrypt.RedactedData), + "code": structpb.NewStringValue(encrypt.RedactedData), }, }, }, diff --git a/internal/gen/controller/api/services/target_service.pb.go b/internal/gen/controller/api/services/target_service.pb.go index c6a919623..65620ce83 100644 --- a/internal/gen/controller/api/services/target_service.pb.go +++ b/internal/gen/controller/api/services/target_service.pb.go @@ -1546,6 +1546,7 @@ type AddTargetCredentialSourcesRequest struct { // 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"` ApplicationCredentialSourceIds []string `protobuf:"bytes,3,rep,name=application_credential_source_ids,proto3" json:"application_credential_source_ids,omitempty"` + EgressCredentialSourceIds []string `protobuf:"bytes,4,rep,name=egress_credential_source_ids,proto3" json:"egress_credential_source_ids,omitempty"` } func (x *AddTargetCredentialSourcesRequest) Reset() { @@ -1601,6 +1602,13 @@ func (x *AddTargetCredentialSourcesRequest) GetApplicationCredentialSourceIds() return nil } +func (x *AddTargetCredentialSourcesRequest) GetEgressCredentialSourceIds() []string { + if x != nil { + return x.EgressCredentialSourceIds + } + return nil +} + type AddTargetCredentialSourcesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1648,9 +1656,8 @@ func (x *AddTargetCredentialSourcesResponse) GetItem() *targets.Target { return nil } -// Sets the values for application_credential_source_ids. If any -// credential_source_id field that is not set in the request will result in -// those fields being cleared. +// Sets the values for credential sources. Any credential_source_id field that +// is not set in the request will result in those fields being cleared. type SetTargetCredentialSourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1661,6 +1668,7 @@ type SetTargetCredentialSourcesRequest struct { // 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"` ApplicationCredentialSourceIds []string `protobuf:"bytes,3,rep,name=application_credential_source_ids,proto3" json:"application_credential_source_ids,omitempty"` + EgressCredentialSourceIds []string `protobuf:"bytes,4,rep,name=egress_credential_source_ids,proto3" json:"egress_credential_source_ids,omitempty"` } func (x *SetTargetCredentialSourcesRequest) Reset() { @@ -1716,6 +1724,13 @@ func (x *SetTargetCredentialSourcesRequest) GetApplicationCredentialSourceIds() return nil } +func (x *SetTargetCredentialSourcesRequest) GetEgressCredentialSourceIds() []string { + if x != nil { + return x.EgressCredentialSourceIds + } + return nil +} + type SetTargetCredentialSourcesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1773,6 +1788,7 @@ type RemoveTargetCredentialSourcesRequest struct { // 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"` ApplicationCredentialSourceIds []string `protobuf:"bytes,3,rep,name=application_credential_source_ids,json=application_credential_source_id,proto3" json:"application_credential_source_ids,omitempty"` + EgressCredentialSourceIds []string `protobuf:"bytes,4,rep,name=egress_credential_source_ids,proto3" json:"egress_credential_source_ids,omitempty"` } func (x *RemoveTargetCredentialSourcesRequest) Reset() { @@ -1828,6 +1844,13 @@ func (x *RemoveTargetCredentialSourcesRequest) GetApplicationCredentialSourceIds return nil } +func (x *RemoveTargetCredentialSourcesRequest) GetEgressCredentialSourceIds() []string { + if x != nil { + return x.EgressCredentialSourceIds + } + return nil +} + type RemoveTargetCredentialSourcesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2204,7 +2227,7 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{ 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x22, 0x9b, 0x01, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x65, 0x6d, 0x22, 0xdf, 0x01, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, @@ -2214,325 +2237,338 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{ 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x22, 0x65, 0x0a, 0x22, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x9b, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x74, + 0x73, 0x12, 0x42, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x65, 0x0a, 0x22, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xdf, 0x01, 0x0a, + 0x21, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x21, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x42, 0x0a, 0x1c, 0x65, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x65, + 0x0a, 0x22, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xe1, 0x01, 0x0a, 0x24, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x03, 0x28, 0x09, 0x52, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x65, 0x0a, 0x22, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x9d, 0x01, - 0x0a, 0x24, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x4b, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x42, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x20, 0x61, 0x70, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x68, 0x0a, - 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x91, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x69, 0x0a, 0x18, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x97, 0x20, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, - 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x9a, 0x01, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x2e, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x92, 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, - 0x6c, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x12, 0xaf, 0x01, 0x0a, 0x0c, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, - 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, - 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xad, 0x01, 0x0a, - 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xa1, 0x01, 0x0a, - 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x12, 0xcc, 0x01, 0x0a, 0x10, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x2d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x62, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x73, 0x20, 0x61, 0x20, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x12, - 0xda, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, - 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x58, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, - 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x92, 0x41, 0x26, 0x12, 0x24, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xd7, 0x01, 0x0a, - 0x11, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, - 0x74, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x68, 0x0a, 0x25, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x22, 0x91, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x69, 0x0a, 0x18, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x39, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x32, 0x97, 0x20, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, + 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x4c, 0x69, + 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x92, + 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x12, 0xaf, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x55, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, - 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x23, 0x12, 0x21, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, - 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xe4, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, - 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x59, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, - 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x24, 0x12, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, - 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xe9, 0x01, - 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3a, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1a, 0x12, 0x18, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xcc, 0x01, 0x0a, + 0x10, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, - 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x29, - 0x12, 0x27, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, - 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, - 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x53, 0x65, - 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x2d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x73, + 0x20, 0x61, 0x20, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x12, 0xda, 0x01, 0x0a, 0x11, + 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, + 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, + 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, + 0x41, 0x26, 0x12, 0x24, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xd7, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x74, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x34, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, + 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, + 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x23, 0x12, + 0x21, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, + 0x65, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x2e, 0x12, 0xe4, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x59, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, + 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x92, 0x41, 0x24, 0x12, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, + 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x41, 0x64, + 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, + 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, - 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x29, 0x12, 0x27, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xf3, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, + 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x29, 0x12, 0x27, 0x41, 0x64, + 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, + 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x5e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, + 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x29, 0x12, 0x27, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x12, 0xf3, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2f, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, - 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x92, 0x41, 0x27, 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, 0x6f, - 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x98, 0x02, 0x0a, 0x1c, - 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x75, 0x88, 0x02, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, - 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, - 0x35, 0x12, 0x33, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, - 0x73, 0x65, 0x20, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, - 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x12, 0x98, 0x02, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x24, + 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x27, + 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x98, 0x02, 0x0a, 0x1c, 0x41, 0x64, 0x64, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x75, 0x88, 0x02, 0x01, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, - 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x35, 0x12, 0x33, 0x44, 0x65, - 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, 0x20, 0x53, 0x65, - 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, - 0x2e, 0x12, 0xa7, 0x02, 0x0a, 0x1f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x42, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x75, 0x88, 0x02, 0x01, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x35, 0x12, 0x33, 0x44, + 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, 0x20, 0x41, + 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, + 0x64, 0x2e, 0x12, 0x98, 0x02, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x69, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x75, 0x88, 0x02, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x35, 0x12, 0x33, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, 0x20, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x12, 0xa7, 0x02, + 0x0a, 0x1f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x12, 0x42, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7b, - 0x88, 0x02, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x22, 0x2c, 0x2f, 0x76, 0x31, 0x2f, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x38, 0x12, 0x36, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, - 0x20, 0x75, 0x73, 0x65, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x12, 0x87, 0x02, 0x0a, 0x1a, - 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7b, 0x88, 0x02, 0x01, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x22, 0x2c, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x38, 0x12, + 0x36, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x12, 0x87, 0x02, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, + 0x61, 0x64, 0x64, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, + 0x41, 0x2f, 0x12, 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x12, 0x84, 0x02, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2c, 0x12, 0x2a, 0x53, 0x65, + 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x91, 0x02, 0x0a, 0x1d, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2f, 0x12, 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x84, 0x02, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, - 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2c, - 0x12, 0x2a, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x12, 0x91, 0x02, 0x0a, - 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x40, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x22, 0x2a, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, - 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x22, 0x2a, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2d, 0x12, + 0x2b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x42, 0x4d, 0x5a, 0x4b, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/internal/kms/immutable_fields_test.go b/internal/kms/immutable_fields_test.go index 4662c3fa9..12f38ed09 100644 --- a/internal/kms/immutable_fields_test.go +++ b/internal/kms/immutable_fields_test.go @@ -14,12 +14,503 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -// TODO (jimlambrt 8/2020) define tests for immutable fields in the remaining -// kms tables: -// kms_oplog_key -// kms_oplog_key_version -// kms_session_key -// kms_session_key_version +func TestOplogKey_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocOplogKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + new := kms.TestOplogKey(t, conn, rk.PrivateId) + + tests := []struct { + name string + update *kms.OplogKey + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.OplogKey { + k := new.Clone().(*kms.OplogKey) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.OplogKey { + k := new.Clone().(*kms.OplogKey) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "root_key_id", + update: func() *kms.OplogKey { + k := new.Clone().(*kms.OplogKey) + k.RootKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"ScopeId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.OplogKey), after.(*kms.OplogKey))) + }) + } +} + +func TestOplogKeyVersion_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + _, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId) + dk := kms.TestOplogKey(t, conn, rk.PrivateId) + new := kms.TestOplogKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("oplog key")) + + tests := []struct { + name string + update *kms.OplogKeyVersion + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "oplog_key_id", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.OplogKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "root_key_version_id", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.RootKeyVersionId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "version", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.Version = uint32(22) + return k + }(), + fieldMask: []string{"Version"}, + }, + { + name: "key", + update: func() *kms.OplogKeyVersion { + k := new.Clone().(*kms.OplogKeyVersion) + k.Key = []byte("updated key") + return k + }(), + fieldMask: []string{"CtKey"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + err = tt.update.Encrypt(context.Background(), wrapper) + require.NoError(err) + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.OplogKeyVersion), after.(*kms.OplogKeyVersion))) + }) + } +} + +func TestTokenKey_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocTokenKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + new := kms.TestTokenKey(t, conn, rk.PrivateId) + + tests := []struct { + name string + update *kms.TokenKey + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.TokenKey { + k := new.Clone().(*kms.TokenKey) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.TokenKey { + k := new.Clone().(*kms.TokenKey) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "root_key_id", + update: func() *kms.TokenKey { + k := new.Clone().(*kms.TokenKey) + k.RootKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"ScopeId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.TokenKey), after.(*kms.TokenKey))) + }) + } +} + +func TestTokenKeyVersion_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + _, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId) + dk := kms.TestTokenKey(t, conn, rk.PrivateId) + new := kms.TestTokenKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("token key")) + + tests := []struct { + name string + update *kms.TokenKeyVersion + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "token_key_id", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.TokenKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "root_key_version_id", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.RootKeyVersionId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "version", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.Version = uint32(22) + return k + }(), + fieldMask: []string{"Version"}, + }, + { + name: "key", + update: func() *kms.TokenKeyVersion { + k := new.Clone().(*kms.TokenKeyVersion) + k.Key = []byte("updated key") + return k + }(), + fieldMask: []string{"CtKey"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + err = tt.update.Encrypt(context.Background(), wrapper) + require.NoError(err) + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.TokenKeyVersion), after.(*kms.TokenKeyVersion))) + }) + } +} + +func TestSessionKey_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocSessionKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + new := kms.TestSessionKey(t, conn, rk.PrivateId) + + tests := []struct { + name string + update *kms.SessionKey + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.SessionKey { + k := new.Clone().(*kms.SessionKey) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.SessionKey { + k := new.Clone().(*kms.SessionKey) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "root_key_id", + update: func() *kms.SessionKey { + k := new.Clone().(*kms.SessionKey) + k.RootKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"ScopeId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.SessionKey), after.(*kms.SessionKey))) + }) + } +} + +func TestSessionKeyVersion_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + _, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId) + dk := kms.TestSessionKey(t, conn, rk.PrivateId) + new := kms.TestSessionKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("session key")) + + tests := []struct { + name string + update *kms.SessionKeyVersion + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "Session_key_id", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.SessionKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "root_key_version_id", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.RootKeyVersionId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"RootKeyId"}, + }, + { + name: "version", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.Version = uint32(22) + return k + }(), + fieldMask: []string{"Version"}, + }, + { + name: "key", + update: func() *kms.SessionKeyVersion { + k := new.Clone().(*kms.SessionKeyVersion) + k.Key = []byte("updated key") + return k + }(), + fieldMask: []string{"CtKey"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + err = tt.update.Encrypt(context.Background(), wrapper) + require.NoError(err) + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.SessionKeyVersion), after.(*kms.SessionKeyVersion))) + }) + } +} func TestRootKeyVersion_ImmutableFields(t *testing.T) { t.Parallel() @@ -341,6 +832,74 @@ func TestDatabaseKeyVersion_ImmutableFields(t *testing.T) { } } +func TestOidcKey_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error) + require.NoError(t, conn.Where("1=1").Delete(kms.AllocOidcKey()).Error) + rk := kms.TestRootKey(t, conn, org.PublicId) + new := kms.TestOidcKey(t, conn, rk.PrivateId) + + tests := []struct { + name string + update *kms.OidcKey + fieldMask []string + }{ + { + name: "private_id", + update: func() *kms.OidcKey { + k := new.Clone().(*kms.OidcKey) + k.PrivateId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"PrivateId"}, + }, + { + name: "create time", + update: func() *kms.OidcKey { + k := new.Clone().(*kms.OidcKey) + k.CreateTime = &ts + return k + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "root_key_id", + update: func() *kms.OidcKey { + k := new.Clone().(*kms.OidcKey) + k.RootKeyId = "o_thisIsNotAValidId" + return k + }(), + fieldMask: []string{"ScopeId"}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + orig := new.Clone() + err := rw.LookupById(context.Background(), orig) + require.NoError(err) + + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + err = rw.LookupById(context.Background(), after) + require.NoError(err) + + assert.True(proto.Equal(orig.(*kms.OidcKey), after.(*kms.OidcKey))) + }) + } +} + func TestOidcKeyVersion_ImmutableFields(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 11b2ddcdd..9e0f64e36 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -2,9 +2,7 @@ package kms import ( "context" - "crypto/sha256" "fmt" - "io" "sync" "github.com/hashicorp/boundary/internal/db" @@ -13,7 +11,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/wrappers/aead" "github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper" - "golang.org/x/crypto/hkdf" ) // ExternalWrappers holds wrappers defined outside of Boundary, e.g. in its @@ -371,41 +368,3 @@ func (k *Kms) loadDek(ctx context.Context, scopeId string, purpose KeyPurpose, r return multi, nil } - -// DerivedReader returns a reader from which keys can be read, using the -// given wrapper, reader length limit, salt and context info. Salt and info can -// be nil. -// -// Example: -// reader, _ := NewDerivedReader(wrapper, userId, jobId) -// key := ed25519.GenerateKey(reader) -func NewDerivedReader(wrapper wrapping.Wrapper, lenLimit int64, salt, info []byte) (*io.LimitedReader, error) { - const op = "kms.NewDerivedReader" - if wrapper == nil { - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing wrapper") - } - if lenLimit < 20 { - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "lenLimit must be >= 20") - } - var aeadWrapper *aead.Wrapper - switch w := wrapper.(type) { - case *multiwrapper.MultiWrapper: - raw := w.WrapperForKeyID("__base__") - var ok bool - if aeadWrapper, ok = raw.(*aead.Wrapper); !ok { - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "unexpected wrapper type from multiwrapper base") - } - case *aead.Wrapper: - if w.GetKeyBytes() == nil { - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "aead wrapper missing bytes") - } - aeadWrapper = w - default: - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "unknown wrapper type") - } - reader := hkdf.New(sha256.New, aeadWrapper.GetKeyBytes(), salt, info) - return &io.LimitedReader{ - R: reader, - N: lenLimit, - }, nil -} diff --git a/internal/kms/kms_test.go b/internal/kms/kms_test.go index dbd8e964e..0ce3bb44b 100644 --- a/internal/kms/kms_test.go +++ b/internal/kms/kms_test.go @@ -3,19 +3,12 @@ package kms import ( "context" "crypto/rand" - "crypto/sha256" - "io" "testing" "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/types/scope" - wrapping "github.com/hashicorp/go-kms-wrapping" - "github.com/hashicorp/go-kms-wrapping/wrappers/aead" "github.com/hashicorp/go-uuid" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "golang.org/x/crypto/hkdf" ) func TestKms_KeyId(t *testing.T) { @@ -73,101 +66,3 @@ func TestKms_KeyId(t *testing.T) { _, err = kmsCache.GetWrapper(ctx, scope.Global.String(), KeyPurposeDatabase, WithKeyId("foo")) require.Error(err) } - -func TestNewDerivedReader(t *testing.T) { - wrapper := db.TestWrapper(t) - - type args struct { - wrapper wrapping.Wrapper - lenLimit int64 - salt []byte - info []byte - } - tests := []struct { - name string - args args - want *io.LimitedReader - wantErr bool - wantErrCode errors.Code - wantErrContains string - }{ - { - name: "valid-with-salt", - args: args{ - wrapper: wrapper, - lenLimit: 32, - info: nil, - salt: []byte("salt"), - }, - want: &io.LimitedReader{ - R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), nil), - N: 32, - }, - }, - { - name: "valid-with-salt-info", - args: args{ - wrapper: wrapper, - lenLimit: 32, - info: []byte("info"), - salt: []byte("salt"), - }, - want: &io.LimitedReader{ - R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), []byte("info")), - N: 32, - }, - }, - { - name: "nil-wrapper", - args: args{ - wrapper: nil, - lenLimit: 10, - info: []byte("info"), - salt: []byte("salt"), - }, - wantErr: true, - wantErrCode: errors.InvalidParameter, - wantErrContains: "missing wrapper", - }, - { - name: "too-short", - args: args{ - wrapper: wrapper, - lenLimit: 10, - info: []byte("info"), - salt: []byte("salt"), - }, - wantErr: true, - wantErrCode: errors.InvalidParameter, - wantErrContains: "lenLimit must be >= 20", - }, - { - name: "wrapper-with-no-bytes", - args: args{ - wrapper: &aead.Wrapper{}, - lenLimit: 32, - info: nil, - salt: []byte("salt"), - }, - wantErr: true, - wantErrCode: errors.InvalidParameter, - wantErrContains: "missing bytes", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - got, err := NewDerivedReader(tt.args.wrapper, tt.args.lenLimit, tt.args.salt, tt.args.info) - if tt.wantErr { - require.Error(err) - assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error: %s", err) - if tt.wantErrContains != "" { - assert.Contains(err.Error(), tt.wantErrContains) - } - return - } - require.NoError(err) - assert.Equal(tt.want, got) - }) - } -} diff --git a/internal/kms/repository_database_key.go b/internal/kms/repository_database_key.go index f9a27f405..94565b82c 100644 --- a/internal/kms/repository_database_key.go +++ b/internal/kms/repository_database_key.go @@ -110,7 +110,7 @@ func (r *Repository) LookupDatabaseKey(ctx context.Context, privateId string, _ // repository returning a count of the number of records deleted. All options // are ignored. func (r *Repository) DeleteDatabaseKey(ctx context.Context, privateId string, _ ...Option) (int, error) { - const op = "kms.(Repository).LookupDatabaseKey" + const op = "kms.(Repository).DeleteDatabaseKey" if privateId == "" { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing private id") } diff --git a/internal/kms/repository_oidc_key.go b/internal/kms/repository_oidc_key.go index b43676ec5..892532401 100644 --- a/internal/kms/repository_oidc_key.go +++ b/internal/kms/repository_oidc_key.go @@ -110,7 +110,7 @@ func (r *Repository) LookupOidcKey(ctx context.Context, privateId string, _ ...O // repository returning a count of the number of records deleted. All options // are ignored. func (r *Repository) DeleteOidcKey(ctx context.Context, privateId string, _ ...Option) (int, error) { - const op = "kms.(Repository).LookupOidcKey" + const op = "kms.(Repository).DeleteOidcKey" if privateId == "" { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing private id") } diff --git a/internal/kms/testing.go b/internal/kms/testing.go index 37ec6da7e..2eae274e2 100644 --- a/internal/kms/testing.go +++ b/internal/kms/testing.go @@ -220,7 +220,7 @@ func TestOidcKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrappin require.NotEmpty(rootKeyVersionId) k, err := NewOidcKeyVersion(oidcKeyId, key, rootKeyVersionId) require.NoError(err) - id, err := newDatabaseKeyVersionId() + id, err := newOidcKeyVersionId() require.NoError(err) k.PrivateId = id err = k.Encrypt(context.Background(), rootKeyVersionWrapper) diff --git a/internal/libs/crypto/derived_reader.go b/internal/libs/crypto/derived_reader.go new file mode 100644 index 000000000..387d65d4d --- /dev/null +++ b/internal/libs/crypto/derived_reader.go @@ -0,0 +1,50 @@ +package crypto + +import ( + "crypto/sha256" + "fmt" + "io" + + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/hashicorp/go-kms-wrapping/wrappers/aead" + "github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper" + "golang.org/x/crypto/hkdf" +) + +// DerivedReader returns a reader from which keys can be read, using the +// given wrapper, reader length limit, salt and context info. Salt and info can +// be nil. +// +// Example: +// reader, _ := NewDerivedReader(wrapper, userId, jobId) +// key := ed25519.GenerateKey(reader) +func NewDerivedReader(wrapper wrapping.Wrapper, lenLimit int64, salt, info []byte) (*io.LimitedReader, error) { + const op = "crypto.NewDerivedReader" + if wrapper == nil { + return nil, fmt.Errorf("%s: missing wrapper: %w", op, ErrInvalidParameter) + } + if lenLimit < 20 { + return nil, fmt.Errorf("%s: lenLimit must be >= 20: %w", op, ErrInvalidParameter) + } + var aeadWrapper *aead.Wrapper + switch w := wrapper.(type) { + case *multiwrapper.MultiWrapper: + raw := w.WrapperForKeyID("__base__") + var ok bool + if aeadWrapper, ok = raw.(*aead.Wrapper); !ok { + return nil, fmt.Errorf("%s: unexpected wrapper type from multiwrapper base: %w", op, ErrInvalidParameter) + } + case *aead.Wrapper: + if w.GetKeyBytes() == nil { + return nil, fmt.Errorf("%s: aead wrapper missing bytes: %w", op, ErrInvalidParameter) + } + aeadWrapper = w + default: + return nil, fmt.Errorf("%s: unknown wrapper type: %w", op, ErrInvalidParameter) + } + reader := hkdf.New(sha256.New, aeadWrapper.GetKeyBytes(), salt, info) + return &io.LimitedReader{ + R: reader, + N: lenLimit, + }, nil +} diff --git a/internal/libs/crypto/derived_reader_test.go b/internal/libs/crypto/derived_reader_test.go new file mode 100644 index 000000000..bcd6d86a9 --- /dev/null +++ b/internal/libs/crypto/derived_reader_test.go @@ -0,0 +1,111 @@ +package crypto + +import ( + "crypto/sha256" + "io" + "testing" + + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/hashicorp/go-kms-wrapping/wrappers/aead" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/hkdf" +) + +func TestNewDerivedReader(t *testing.T) { + wrapper := TestWrapper(t) + + type args struct { + wrapper wrapping.Wrapper + lenLimit int64 + salt []byte + info []byte + } + tests := []struct { + name string + args args + want *io.LimitedReader + wantErr bool + wantErrCode error + wantErrContains string + }{ + { + name: "valid-with-salt", + args: args{ + wrapper: wrapper, + lenLimit: 32, + info: nil, + salt: []byte("salt"), + }, + want: &io.LimitedReader{ + R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), nil), + N: 32, + }, + }, + { + name: "valid-with-salt-info", + args: args{ + wrapper: wrapper, + lenLimit: 32, + info: []byte("info"), + salt: []byte("salt"), + }, + want: &io.LimitedReader{ + R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), []byte("info")), + N: 32, + }, + }, + { + name: "nil-wrapper", + args: args{ + wrapper: nil, + lenLimit: 10, + info: []byte("info"), + salt: []byte("salt"), + }, + wantErr: true, + wantErrCode: ErrInvalidParameter, + wantErrContains: "missing wrapper", + }, + { + name: "too-short", + args: args{ + wrapper: wrapper, + lenLimit: 10, + info: []byte("info"), + salt: []byte("salt"), + }, + wantErr: true, + wantErrCode: ErrInvalidParameter, + wantErrContains: "lenLimit must be >= 20", + }, + { + name: "wrapper-with-no-bytes", + args: args{ + wrapper: &aead.Wrapper{}, + lenLimit: 32, + info: nil, + salt: []byte("salt"), + }, + wantErr: true, + wantErrCode: ErrInvalidParameter, + wantErrContains: "missing bytes", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewDerivedReader(tt.args.wrapper, tt.args.lenLimit, tt.args.salt, tt.args.info) + if tt.wantErr { + require.Error(err) + assert.ErrorIsf(err, tt.wantErrCode, "unexpected error: %s", err) + if tt.wantErrContains != "" { + assert.Contains(err.Error(), tt.wantErrContains) + } + return + } + require.NoError(err) + assert.Equal(tt.want, got) + }) + } +} diff --git a/internal/libs/crypto/error.go b/internal/libs/crypto/error.go new file mode 100644 index 000000000..6e97908cc --- /dev/null +++ b/internal/libs/crypto/error.go @@ -0,0 +1,9 @@ +package crypto + +import ( + "errors" +) + +var ( + ErrInvalidParameter = errors.New("Invalid parameter") +) diff --git a/internal/libs/crypto/hmac_sha256.go b/internal/libs/crypto/hmac_sha256.go new file mode 100644 index 000000000..05491e6ef --- /dev/null +++ b/internal/libs/crypto/hmac_sha256.go @@ -0,0 +1,94 @@ +package crypto + +import ( + "context" + "crypto/ed25519" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + + wrapping "github.com/hashicorp/go-kms-wrapping" + "golang.org/x/crypto/blake2b" +) + +// HmacSha256WithPrk will HmacSha256 using the provided prk. See HmacSha256 for +// options supported. +func HmacSha256WithPrk(ctx context.Context, data, prk []byte, opt ...Option) (string, error) { + opt = append(opt, WithPrk(prk)) + return HmacSha256(ctx, data, nil, nil, nil, opt...) +} + +// HmacSha256 the provided data. Supports WithPrefix, WithEd25519 and WithPrk +// options. WithEd25519 is a "legacy" way to complete this operation and should +// not be used in new operations unless backward compatibility is needed. The +// WithPrefix option will prepend the prefix to the hmac-sha256 value. +func HmacSha256(ctx context.Context, data []byte, cipher wrapping.Wrapper, salt, info []byte, opt ...Option) (string, error) { + const op = "crypto.HmacSha256" + opts, err := getOpts(opt...) + if err != nil { + return "", fmt.Errorf("%s: unable to get options: %w", op, err) + } + if data == nil { + return "", fmt.Errorf("%s: missing data: %w", op, ErrInvalidParameter) + } + if cipher == nil && opts.withPrk == nil { + return "", fmt.Errorf("%s: you must specify either a wrapper or prk: %w", op, ErrInvalidParameter) + } + if cipher != nil && opts.withPrk != nil { + return "", fmt.Errorf("%s: you cannot specify both a wrapper or prk: %w", op, ErrInvalidParameter) + } + if opts.withEd25519 && opts.withPrk != nil { + return "", fmt.Errorf("%s: you cannot specify both ed25519 and a prk: %w", op, ErrInvalidParameter) + } + var key [32]byte + switch { + case opts.withPrk != nil: + key = blake2b.Sum256(opts.withPrk) + + case opts.withEd25519: + reader, err := NewDerivedReader(cipher, 32, salt, info) + if err != nil { + return "", fmt.Errorf("%s: %w", op, err) + } + edKey, _, err := ed25519.GenerateKey(reader) + if err != nil { + return "", fmt.Errorf("%s: unable to generate derived key: %w", op, ErrInvalidParameter) + } + n := copy(key[:], edKey) + if n != 32 { + return "", fmt.Errorf("%s: expected to copy 32 bytes and got: %d", op, n) + } + + default: + reader, err := NewDerivedReader(cipher, 32, salt, info) + if err != nil { + return "", fmt.Errorf("%s: %w", op, err) + } + readerKey := make([]byte, 32) + n, err := io.ReadFull(reader, readerKey) + if err != nil { + return "", fmt.Errorf("%s: %w", op, err) + } + if n != 32 { + return "", fmt.Errorf("%s: expected to read 32 bytes and got: %d", op, n) + } + key = blake2b.Sum256(readerKey) + } + mac := hmac.New(sha256.New, key[:]) + _, _ = mac.Write(data) + hmac := mac.Sum(nil) + + var hmacString string + switch opts.withBase64Encoding { + case true: + hmacString = base64.RawURLEncoding.EncodeToString(hmac) + case false: + hmacString = string(hmac) + } + if opts.withPrefix != "" { + return opts.withPrefix + hmacString, nil + } + return hmacString, nil +} diff --git a/internal/libs/crypto/hmac_sha256_test.go b/internal/libs/crypto/hmac_sha256_test.go new file mode 100644 index 000000000..41782c830 --- /dev/null +++ b/internal/libs/crypto/hmac_sha256_test.go @@ -0,0 +1,168 @@ +package crypto + +import ( + "context" + "crypto/ed25519" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "io" + "testing" + + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/blake2b" +) + +func Test_HmacSha256(t *testing.T) { + testCtx := context.Background() + testWrapper := TestWrapper(t) + tests := []struct { + name string + data []byte + wrapper wrapping.Wrapper + salt []byte + info []byte + opts []Option + wantHmac string + wantErr bool + wantErrIs error + }{ + { + name: "missing data", + wrapper: testWrapper, + wantErr: true, + wantErrIs: ErrInvalidParameter, + }, + { + name: "missing wrapper", + data: []byte("test"), + wantErr: true, + wantErrIs: ErrInvalidParameter, + }, + { + name: "prk-and-ed25519", + data: []byte("test"), + wrapper: testWrapper, + opts: []Option{WithPrk([]byte("prk")), WithEd25519()}, + wantErr: true, + wantErrIs: ErrInvalidParameter, + }, + { + name: "prk-and-wrapper", + data: []byte("test"), + wrapper: testWrapper, + opts: []Option{WithPrk([]byte("prk")), WithEd25519()}, + wantErr: true, + wantErrIs: ErrInvalidParameter, + }, + { + name: "blake2b-with-prefix", + data: []byte("test"), + wrapper: testWrapper, + opts: []Option{WithPrefix("prefix:")}, + wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrefix("prefix:")), + }, + { + name: "blake2b-with-prefix-with-bas64", + data: []byte("test"), + wrapper: testWrapper, + opts: []Option{WithPrefix("prefix:"), WithBase64Encoding()}, + wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrefix("prefix:"), WithBase64Encoding()), + }, + { + name: "with-prk", + data: []byte("test"), + opts: []Option{WithPrk([]byte("prk-0123456789012345678901234567890"))}, + wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrk([]byte("prk-0123456789012345678901234567890"))), + }, + { + name: "withEd25519", + data: []byte("test"), + wrapper: testWrapper, + opts: []Option{WithEd25519()}, + wantHmac: testWithEd25519(t, []byte("test"), testWrapper, nil, nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + hm, err := HmacSha256(testCtx, tt.data, tt.wrapper, tt.salt, tt.info, tt.opts...) + if tt.wantErr { + require.Error(err) + if tt.wantErrIs != nil { + assert.ErrorIs(err, tt.wantErrIs) + } + return + } + require.NoError(err) + assert.Equal(tt.wantHmac, hm) + }) + } + + t.Run("HmacSha256WithPrk", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + hm, err := HmacSha256WithPrk(testCtx, []byte("test"), []byte("prk-0123456789012345678901234567890")) + require.NoError(err) + want := testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrk([]byte("prk-0123456789012345678901234567890"))) + assert.Equal(want, hm) + }) +} + +func testWithEd25519(t *testing.T, data []byte, w wrapping.Wrapper, salt, info []byte, opt ...Option) string { + t.Helper() + require := require.New(t) + reader, err := NewDerivedReader(w, 32, salt, info) + require.NoError(err) + edKey, _, err := ed25519.GenerateKey(reader) + require.NoError(err) + var key [32]byte + n := copy(key[:], edKey) + require.Equal(n, 32) + return testHmac(t, key[:], data, opt...) +} + +func testWithBlake2b(t *testing.T, data []byte, w wrapping.Wrapper, salt, info []byte, opt ...Option) string { + t.Helper() + require := require.New(t) + require.NotNil(data) + require.NotNil(w) + opts, err := getOpts(opt...) + require.NoError(err) + var key [32]byte + switch { + case opts.withPrk != nil: + key = blake2b.Sum256(opts.withPrk) + default: + reader, err := NewDerivedReader(w, 32, salt, info) + require.NoError(err) + readerKey := make([]byte, 32) + n, err := io.ReadFull(reader, readerKey) + require.NoError(err) + require.Equal(n, 32) + key = blake2b.Sum256(readerKey) + } + return testHmac(t, key[:], data, opt...) +} + +func testHmac(t *testing.T, key, data []byte, opt ...Option) string { + t.Helper() + require := require.New(t) + mac := hmac.New(sha256.New, key) + _, _ = mac.Write(data) + hmac := mac.Sum(nil) + var hmacString string + opts, err := getOpts(opt...) + require.NoError(err) + switch opts.withBase64Encoding { + case true: + hmacString = base64.RawURLEncoding.EncodeToString(hmac) + case false: + hmacString = string(hmac) + } + if opts.withPrefix != "" { + return opts.withPrefix + hmacString + } + return hmacString +} diff --git a/internal/libs/crypto/options.go b/internal/libs/crypto/options.go new file mode 100644 index 000000000..e3a88e312 --- /dev/null +++ b/internal/libs/crypto/options.go @@ -0,0 +1,63 @@ +package crypto + +// getOpts - iterate the inbound Options and return a struct. +func getOpts(opt ...Option) (*options, error) { + opts := getDefaultOptions() + for _, o := range opt { + if o != nil { + if err := o(opts); err != nil { + return nil, err + } + } + } + return opts, nil +} + +// Option - how Options are passed as arguments. +type Option func(*options) error + +// options = how options are represented +type options struct { + withPrefix string + withPrk []byte + withEd25519 bool + withBase64Encoding bool +} + +func getDefaultOptions() *options { + return &options{} +} + +// WithPrefix allows an optional prefix to be specified for the data returned +func WithPrefix(prefix string) Option { + return func(o *options) error { + o.withPrefix = prefix + return nil + } +} + +// WithPrk allows an optional PRK (pseudorandom key) to be specified for an +// operation. If you're using this option with HmacSha256, you might consider +// using HmacSha256WithPrk instead. +func WithPrk(prk []byte) Option { + return func(o *options) error { + o.withPrk = prk + return nil + } +} + +// WithEd25519 allows an optional request to use ed25519 during the operation +func WithEd25519() Option { + return func(o *options) error { + o.withEd25519 = true + return nil + } +} + +// WithBase64Encoding allows an optional request to base64 encode the data returned +func WithBase64Encoding() Option { + return func(o *options) error { + o.withBase64Encoding = true + return nil + } +} diff --git a/internal/libs/crypto/options_test.go b/internal/libs/crypto/options_test.go new file mode 100644 index 000000000..f44b87eda --- /dev/null +++ b/internal/libs/crypto/options_test.go @@ -0,0 +1,45 @@ +package crypto + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Test_GetOpts provides unit tests for GetOpts and all the options +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithPrefix", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + opts, err := getOpts(WithPrefix("test")) + require.NoError(err) + testOpts := getDefaultOptions() + testOpts.withPrefix = "test" + assert.Equal(opts, testOpts) + }) + t.Run("WithPrk", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + opts, err := getOpts(WithPrk([]byte("test"))) + require.NoError(err) + testOpts := getDefaultOptions() + testOpts.withPrk = []byte("test") + assert.Equal(opts, testOpts) + }) + t.Run("WithEd25519", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + opts, err := getOpts(WithEd25519()) + require.NoError(err) + testOpts := getDefaultOptions() + testOpts.withEd25519 = true + assert.Equal(opts, testOpts) + }) + t.Run("WithBase64Encoding", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + opts, err := getOpts(WithBase64Encoding()) + require.NoError(err) + testOpts := getDefaultOptions() + testOpts.withBase64Encoding = true + assert.Equal(opts, testOpts) + }) +} diff --git a/internal/libs/crypto/testing.go b/internal/libs/crypto/testing.go new file mode 100644 index 000000000..d470cebc8 --- /dev/null +++ b/internal/libs/crypto/testing.go @@ -0,0 +1,33 @@ +package crypto + +import ( + "crypto/rand" + "encoding/base64" + "testing" + + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/hashicorp/go-kms-wrapping/wrappers/aead" +) + +// TestWrapper initializes an AEAD wrapping.Wrapper for testing +func TestWrapper(t *testing.T) wrapping.Wrapper { + rootKey := make([]byte, 32) + n, err := rand.Read(rootKey) + if err != nil { + t.Fatal(err) + } + if n != 32 { + t.Fatal(n) + } + root := aead.NewWrapper(nil) + _, err = root.SetConfig(map[string]string{ + "key_id": base64.StdEncoding.EncodeToString(rootKey), + }) + if err != nil { + t.Fatal(err) + } + if err := root.SetAESGCMKeyBytes(rootKey); err != nil { + t.Fatal(err) + } + return root +} diff --git a/internal/observability/event/context_test.go b/internal/observability/event/context_test.go index f8a1c1555..67c402e38 100644 --- a/internal/observability/event/context_test.go +++ b/internal/observability/event/context_test.go @@ -662,7 +662,7 @@ func Test_WriteAudit(t *testing.T) { AuthAccountId: "test_auth_account_id", }, GrantsInfo: &event.GrantsInfo{ - Grants: []event.GrantsPair{ + Grants: []event.Grant{ { Grant: "test_grant", ScopeId: "test_grant_scope_id", diff --git a/internal/observability/event/event.go b/internal/observability/event/event.go index 53fb9ba8d..2de944ac8 100644 --- a/internal/observability/event/event.go +++ b/internal/observability/event/event.go @@ -25,20 +25,22 @@ type UserInfo struct { } type GrantsInfo struct { - Grants []GrantsPair `json:"grants_pair,omitempty"` + Grants []Grant `json:"grants,omitempty"` } -type GrantsPair struct { +type Grant struct { Grant string `json:"grant,omitempty" class:"public"` ScopeId string `json:"scope_id,omitempty" class:"public"` + RoleId string `json:"role_id,omitempty" class:"public"` } type Auth struct { - AuthTokenId string `json:"auth_token_id" class:"public"` - UserInfo *UserInfo `json:"user_info,omitempty"` // boundary field - GrantsInfo *GrantsInfo `json:"grants_info,omitempty"` - UserEmail string `json:"email,omitempty" class:"sensitive"` - UserName string `json:"name,omitempty" class:"sensitive"` + DisabledAuthEntirely *bool `json:"disabled_auth_entirely,omitempty" class:"public"` + AuthTokenId string `json:"auth_token_id" class:"public"` + UserInfo *UserInfo `json:"user_info,omitempty"` // boundary field + GrantsInfo *GrantsInfo `json:"grants_info,omitempty"` + UserEmail string `json:"email,omitempty" class:"sensitive"` + UserName string `json:"name,omitempty" class:"sensitive"` } type Request struct { diff --git a/internal/proto/local/controller/api/resources/targets/v1/target.proto b/internal/proto/local/controller/api/resources/targets/v1/target.proto index 368995f3f..d52173522 100644 --- a/internal/proto/local/controller/api/resources/targets/v1/target.proto +++ b/internal/proto/local/controller/api/resources/targets/v1/target.proto @@ -143,11 +143,10 @@ message Target { // Output only. The application credential sources associated with this Target. repeated CredentialSource application_credential_sources = 410 [json_name="application_credential_sources"]; - // TODO: Uncomment when we suport more purposes beyond application -// // Output only. The egress credential libraries associated with this Target. -// repeated CredentialLibrary egress_credential_libraries = 190 [json_name="egress_credential_libraries"]; -// // Output only. The credential libraries associated with this Target. -// repeated CredentialLibrary ingress_credential_libraries = 200 [json_name="ingress_credential_libraries"]; + // Output only. The IDs of the egress credential source ids associated with this Target. + repeated string egress_credential_source_ids = 500 [json_name="egress_credential_source_ids"]; + // Output only. The egress credential sources associated with this Target. + repeated CredentialSource egress_credential_sources = 510 [json_name="egress_credential_sources"]; // The attributes that are applicable for the specific Target. google.protobuf.Struct attributes = 200 [(custom_options.v1.generate_sdk_option) = true]; diff --git a/internal/proto/local/controller/api/services/v1/target_service.proto b/internal/proto/local/controller/api/services/v1/target_service.proto index c0f61474a..22f6d27d1 100644 --- a/internal/proto/local/controller/api/services/v1/target_service.proto +++ b/internal/proto/local/controller/api/services/v1/target_service.proto @@ -475,21 +475,22 @@ message AddTargetCredentialSourcesRequest { // The mutation will fail if the version does not match the latest known good version. uint32 version = 2; repeated string application_credential_source_ids = 3 [json_name="application_credential_source_ids"]; + repeated string egress_credential_source_ids = 4 [json_name="egress_credential_source_ids"]; } message AddTargetCredentialSourcesResponse { api.resources.targets.v1.Target item = 1; } -// Sets the values for application_credential_source_ids. If any -// credential_source_id field that is not set in the request will result in -// those fields being cleared. +// Sets the values for credential sources. Any credential_source_id field that +// is not set in the request will result in those fields being cleared. message SetTargetCredentialSourcesRequest { string id = 1; // 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; repeated string application_credential_source_ids = 3 [json_name="application_credential_source_ids"]; + repeated string egress_credential_source_ids = 4 [json_name="egress_credential_source_ids"]; } message SetTargetCredentialSourcesResponse { @@ -502,6 +503,7 @@ message RemoveTargetCredentialSourcesRequest { // The mutation will fail if the version does not match the latest known good version. uint32 version = 2; repeated string application_credential_source_ids = 3 [json_name="application_credential_source_id"]; + repeated string egress_credential_source_ids = 4 [json_name="egress_credential_source_ids"]; } message RemoveTargetCredentialSourcesResponse { diff --git a/internal/proto/local/controller/storage/db/db_test/v1/db_test.proto b/internal/proto/local/controller/storage/db/db_test/v1/db_test.proto index 22917ef95..4519f5932 100644 --- a/internal/proto/local/controller/storage/db/db_test/v1/db_test.proto +++ b/internal/proto/local/controller/storage/db/db_test/v1/db_test.proto @@ -118,4 +118,27 @@ message StoreTestScooter { // @inject_tag: `gorm:"default:null"` int32 mpg = 7; +} + +// StoreTestAccessory used in the db tests only and provides a gorm resource with +// an id that's not a private or public id +message StoreTestAccessory { + // @inject_tag: gorm:"primary_key" + uint32 accessory_id = 1; + + // @inject_tag: `gorm:"default:not_null"` + string description = 2; +} + +// StoreTestScooterAccessory used in the db tests only and provides a gorm +// resource with multiple pks +message StoreTestScooterAccessory { + // @inject_tag: gorm:"primary_key" + uint32 accessory_id = 1; + + // @inject_tag: gorm:"primary_key" + uint32 scooter_id = 2; + + // @inject_tag: `gorm:"default:null"` + string review = 3; } \ No newline at end of file diff --git a/internal/proto/local/controller/storage/target/targettest/store/v1/target.proto b/internal/proto/local/controller/storage/target/targettest/store/v1/target.proto new file mode 100644 index 000000000..b9471c819 --- /dev/null +++ b/internal/proto/local/controller/storage/target/targettest/store/v1/target.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; + +package controller.storage.target.targettest.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/target/targettest/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; +import "controller/custom_options/v1/options.proto"; + +message Target { + // public_id is used to access the targettest.Target via an API + // @inject_tag: gorm:"primary_key" + string public_id = 10; + + // scope id for the targettest.Target + // @inject_tag: `gorm:"default:null"` + string scope_id = 20; + + // name is the optional friendly name used to + // access the targettest.Target via an API + // @inject_tag: `gorm:"default:null"` + string name = 30 + [(custom_options.v1.mask_mapping) = { this: "name" that: "name" }]; + + // description of the targettest.Target + // @inject_tag: `gorm:"default:null"` + string description = 40 [(custom_options.v1.mask_mapping) = { + this: "description" + that: "description" + }]; + + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 50; + + // update_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 60; + + // version allows optimistic locking of the targettest.Target when modifying the + // targettest.Target + // @inject_tag: `gorm:"default:null"` + uint32 version = 70; + + // default port of the targettest.Target + // @inject_tag: `gorm:"default:null"` + uint32 default_port = 80 [(custom_options.v1.mask_mapping) = { + this: "DefaultPort" + that: "attributes.default_port" + }]; + + // Maximum total lifetime of a created session, in seconds + // @inject_tag: `gorm:"default:null"` + uint32 session_max_seconds = 100 [(custom_options.v1.mask_mapping) = { + this: "SessionMaxSeconds" + that: "session_max_seconds" + }]; + + // Maximum number of connections in a session + // @inject_tag: `gorm:"default:null"` + int32 session_connection_limit = 110 [(custom_options.v1.mask_mapping) = { + this: "SessionConnectionLimit" + that: "session_connection_limit" + }]; + + // A boolean expression that allows filtering the workers that can handle a session + // @inject_tag: `gorm:"default:null"` + string worker_filter = 120 [(custom_options.v1.mask_mapping) = { + this: "WorkerFilter" + that: "worker_filter" + }]; +} + + diff --git a/internal/servers/controller/auth/auth.go b/internal/servers/controller/auth/auth.go index f39a76505..ebc6d23bb 100644 --- a/internal/servers/controller/auth/auth.go +++ b/internal/servers/controller/auth/auth.go @@ -140,6 +140,9 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { v.ctx = ctx + ea := &event.Auth{} + defer event.WriteAudit(ctx, op, event.WithAuth(ea)) + opts := getOpts(opt...) ret.Scope = new(scopes.ScopeInfo) @@ -160,6 +163,8 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { // In tests we often simply disable auth so we can test the service handlers // without fuss if v.requestInfo.DisableAuthEntirely { + yes := true + ea.DisabledAuthEntirely = &yes const op = "auth.(disabled).lookupScope" ret.Scope.Id = v.requestInfo.ScopeIdOverride if ret.Scope.Id == "" { @@ -205,6 +210,7 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { if reqInfo != nil { reqInfo.UserId = ret.UserId } + ea.UserInfo = &event.UserInfo{UserId: ret.UserId} ret.Error = nil return } @@ -226,8 +232,10 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { } var authResults perms.ACLResults + var grantTuples []perms.GrantTuple + var accountId, userName, userEmail string var err error - authResults, ret.UserId, ret.Scope, v.acl, err = v.performAuthCheck(ctx) + authResults, ret.UserId, accountId, userName, userEmail, ret.Scope, v.acl, grantTuples, err = v.performAuthCheck(ctx) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error performing authn/authz check")) return @@ -262,10 +270,31 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { if ret.UserId == AnonymousUserId { ret.Error = handlers.UnauthenticatedError() } + ea.UserInfo = &event.UserInfo{ + UserId: ret.UserId, + } return } } + grants := make([]event.Grant, 0, len(grantTuples)) + for _, g := range grantTuples { + grants = append(grants, event.Grant{ + Grant: g.Grant, + RoleId: g.RoleId, + ScopeId: g.ScopeId, + }) + } + ea.UserInfo = &event.UserInfo{ + UserId: ret.UserId, + AuthAccountId: accountId, + } + ea.GrantsInfo = &event.GrantsInfo{ + Grants: grants, + } + ea.UserName = userName + ea.UserEmail = userEmail + if reqInfo != nil { reqInfo.UserId = ret.UserId reqInfo.OutputFields = authResults.OutputFields @@ -401,15 +430,23 @@ func (v *verifier) decryptToken(ctx context.Context) { } } -func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLResults, userId string, scopeInfo *scopes.ScopeInfo, retAcl perms.ACL, retErr error) { +func (v verifier) performAuthCheck(ctx context.Context) ( + aclResults perms.ACLResults, + userId string, + accountId string, + userName string, + userEmail string, + scopeInfo *scopes.ScopeInfo, + retAcl perms.ACL, + grantTuples []perms.GrantTuple, + retErr error) { const op = "auth.(verifier).performAuthCheck" // Ensure we return an error by default if we forget to set this somewhere - retErr = errors.NewDeprecated(errors.Unknown, op, "default auth error", errors.WithoutEvent()) + retErr = errors.New(ctx, errors.Unknown, op, "default auth error", errors.WithoutEvent()) // Make the linter happy _ = retErr scopeInfo = new(scopes.ScopeInfo) userId = AnonymousUserId - var accountId string // Validate the token and fetch the corresponding user ID switch v.requestInfo.TokenFormat { @@ -428,7 +465,7 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes } tokenRepo, err := v.authTokenRepoFn() if err != nil { - retErr = errors.WrapDeprecated(err, op) + retErr = errors.Wrap(ctx, err, op) return } at, err := tokenRepo.ValidateToken(v.ctx, v.requestInfo.PublicId, v.requestInfo.Token) @@ -451,10 +488,18 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes iamRepo, err := v.iamRepoFn() if err != nil { - retErr = errors.WrapDeprecated(err, op, errors.WithMsg("failed to get iam repo")) + retErr = errors.Wrap(ctx, err, op, errors.WithMsg("failed to get iam repo")) return } + u, _, err := iamRepo.LookupUser(ctx, userId) + if err != nil { + retErr = errors.Wrap(ctx, err, op, errors.WithMsg("failed to lookup user")) + return + } + userEmail = u.Email + userName = u.FullName + // Look up scope details to return. We can skip a lookup when using the // global scope switch v.res.ScopeId { @@ -470,11 +515,11 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes default: scp, err := iamRepo.LookupScope(v.ctx, v.res.ScopeId) if err != nil { - retErr = errors.WrapDeprecated(err, op) + retErr = errors.Wrap(ctx, err, op) return } if scp == nil { - retErr = errors.NewDeprecated(errors.InvalidParameter, op, fmt.Sprint("non-existent scope $q", v.res.ScopeId)) + retErr = errors.New(ctx, errors.InvalidParameter, op, fmt.Sprint("non-existent scope $q", v.res.ScopeId)) return } scopeInfo = &scopes.ScopeInfo{ @@ -495,13 +540,12 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes } var parsedGrants []perms.Grant - var grantTuples []perms.GrantTuple // Fetch and parse grants for this user ID (which may include grants for // u_anon and u_auth) grantTuples, err = iamRepo.GrantsForUser(v.ctx, userId) if err != nil { - retErr = errors.WrapDeprecated(err, op) + retErr = errors.Wrap(ctx, err, op) return } parsedGrants = make([]perms.Grant, 0, len(grantTuples)) @@ -516,7 +560,7 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes perms.WithAccountId(accountId), perms.WithSkipFinalValidation(true)) if err != nil { - retErr = errors.WrapDeprecated(err, op, errors.WithMsg(fmt.Sprintf("failed to parse grant %#v", pair.Grant))) + retErr = errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed to parse grant %#v", pair.Grant))) return } parsedGrants = append(parsedGrants, parsed) diff --git a/internal/servers/controller/auth/auth_test.go b/internal/servers/controller/auth/auth_test.go index e90b523f2..0eda12218 100644 --- a/internal/servers/controller/auth/auth_test.go +++ b/internal/servers/controller/auth/auth_test.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" "net/http/httptest" + "os" + "sync" "testing" "github.com/hashicorp/boundary/internal/authtoken" @@ -12,8 +14,12 @@ import ( authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/tests/api" + "github.com/hashicorp/eventlogger/filters/encrypt" + "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -132,3 +138,125 @@ func TestAuthTokenAuthenticator(t *testing.T) { }) } } + +func TestVerify_AuditEvent(t *testing.T) { + // this cannot run in parallel because it relies on envvar + // globals.BOUNDARY_DEVELOPER_ENABLE_EVENTS + event.TestEnableEventing(t, true) + eventConfig := event.TestEventerConfig(t, "Test_Verify", event.TestWithAuditSink(t)) + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + require.NoError(t, event.InitSysEventer(testLogger, testLock, "Test_Verify", event.WithEventerConfig(&eventConfig.EventerConfig))) + + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + tokenRepo, err := authtoken.NewRepository(rw, rw, testKms) + require.NoError(t, err) + iamRepo := iam.TestRepo(t, conn, wrapper) + tokenRepoFn := func() (*authtoken.Repository, error) { + return tokenRepo, nil + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + serversRepoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, testKms) + } + + o, _ := iam.TestScopes(t, iamRepo) + at := authtoken.TestAuthToken(t, conn, testKms, o.GetPublicId()) + encToken, err := authtoken.EncryptToken(context.Background(), testKms, o.GetPublicId(), at.GetPublicId(), at.GetToken()) + require.NoError(t, err) + + tokValue := at.GetPublicId() + "_" + encToken + jsCookieVal, httpCookieVal := tokValue[:len(tokValue)/2], tokValue[len(tokValue)/2:] + + tests := []struct { + name string + headers map[string]string + cookies []http.Cookie + opt []Option + disableAuth bool + wantResults VerifyResults + wantAuthAuditData bool + wantUserId string + }{ + { + name: "bearer-token", + headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokValue)}, + opt: []Option{WithScopeId(o.PublicId)}, + wantAuthAuditData: true, + wantUserId: at.IamUserId, + }, + { + name: "split-cookie-token", + cookies: []http.Cookie{ + {Name: handlers.HttpOnlyCookieName, Value: httpCookieVal}, + {Name: handlers.JsVisibleCookieName, Value: jsCookieVal}, + }, + opt: []Option{WithScopeId(o.PublicId)}, + wantAuthAuditData: true, + wantUserId: at.IamUserId, + }, + { + name: "no-auth-data", + opt: []Option{WithScopeId(o.PublicId)}, + wantAuthAuditData: true, + wantUserId: "u_anon", + }, + { + name: "disable-auth", + opt: []Option{WithScopeId(o.PublicId)}, + disableAuth: true, + wantAuthAuditData: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + req := httptest.NewRequest("GET", "http://127.0.0.1/v1/scopes/o_1", nil) + for k, v := range tt.headers { + req.Header.Set(k, v) + } + for _, c := range tt.cookies { + req.AddCookie(&c) + } + + // Add values for authn/authz checking + requestInfo := authpb.RequestInfo{ + Path: req.URL.Path, + Method: req.Method, + DisableAuthEntirely: tt.disableAuth, + } + requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = GetTokenFromRequest(context.TODO(), testKms, req) + + ctx := NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, testKms, &requestInfo) + + _ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls + _ = Verify(ctx, tt.opt...) + got := api.CloudEventFromFile(t, eventConfig.AuditEvents.Name()) + + if tt.wantAuthAuditData { + auth, ok := got.Data.(map[string]interface{})["auth"].(map[string]interface{}) + require.True(ok) + assert.Equal(encrypt.RedactedData, auth["email"]) + assert.Equal(encrypt.RedactedData, auth["name"]) + + if tt.disableAuth { + assert.Equal(true, auth["disabled_auth_entirely"]) + } + if tt.wantUserId != "" { + userInfo, ok := auth["user_info"].(map[string]interface{}) + require.True(ok) + assert.Equal(tt.wantUserId, userInfo["id"]) + } + } + }) + } +} diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index 1b1cecdb6..ee01be7b3 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -592,7 +592,7 @@ func (s Service) AddTargetCredentialLibraries(ctx context.Context, req *pbs.AddT if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), req.GetVersion()) + t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) if err != nil { return nil, err } @@ -630,7 +630,7 @@ func (s Service) SetTargetCredentialLibraries(ctx context.Context, req *pbs.SetT if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), req.GetVersion()) + t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) if err != nil { return nil, err } @@ -668,7 +668,7 @@ func (s Service) RemoveTargetCredentialLibraries(ctx context.Context, req *pbs.R if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), req.GetVersion()) + t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) if err != nil { return nil, err } @@ -706,7 +706,7 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetVersion()) + t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { return nil, err } @@ -744,7 +744,7 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetVersion()) + t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { return nil, err } @@ -782,7 +782,7 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem if authResults.Error != nil { return nil, authResults.Error } - t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetVersion()) + t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { return nil, err } @@ -1352,12 +1352,17 @@ func (s Service) removeHostSourcesInRepo(ctx context.Context, targetId string, h return out, hs, cl, nil } -func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string, ids []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { +func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string, applicationIds []string, egressIds []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { repo, err := s.repoFn() if err != nil { return nil, nil, nil, err } - out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, strutil.RemoveDuplicates(ids, false)) + credLibs, err := createCredLibs(targetId, applicationIds, egressIds, nil) + if err != nil { + return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + } + + out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, credLibs) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add credential sources to target: %v.", err) @@ -1368,13 +1373,19 @@ func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string return out, hs, credSources, nil } -func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string, ids []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { +func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string, applicationIds []string, egressIds []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { const op = "targets.(Service).setCredentialSourcesInRepo" repo, err := s.repoFn() if err != nil { return nil, nil, nil, err } - _, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, strutil.RemoveDuplicates(ids, false)) + + credLibs, err := createCredLibs(targetId, applicationIds, egressIds, nil) + if err != nil { + return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + } + + _, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, credLibs) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) @@ -1390,13 +1401,18 @@ func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string return out, hs, credSources, nil } -func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId string, ids []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { +func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId string, applicationIds []string, egressIds []string, version uint32) (target.Target, []target.HostSource, []target.CredentialSource, error) { const op = "targets.(Service).removeCredentialSourcesInRepo" repo, err := s.repoFn() if err != nil { return nil, nil, nil, err } - _, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, strutil.RemoveDuplicates(ids, false)) + + credLibs, err := createCredLibs(targetId, applicationIds, egressIds, nil) + if err != nil { + return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + } + _, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, credLibs) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove credential sources from target: %v.", err) @@ -1917,8 +1933,9 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ if req.GetVersion() == 0 { badFields[globals.VersionField] = "Required field." } - if len(req.GetApplicationCredentialSourceIds()) == 0 { - badFields[globals.ApplicationCredentialSourceIdsField] = "Must be non-empty." + if len(req.GetApplicationCredentialSourceIds())+len(req.GetEgressCredentialSourceIds()) == 0 { + badFields[globals.ApplicationCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." + badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." } for _, cl := range req.GetApplicationCredentialSourceIds() { if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { @@ -1926,6 +1943,12 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ break } } + for _, cl := range req.GetEgressCredentialSourceIds() { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) + break + } + } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) } @@ -1946,6 +1969,12 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ break } } + for _, cl := range req.GetEgressCredentialSourceIds() { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) + break + } + } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) } @@ -1960,8 +1989,9 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc if req.GetVersion() == 0 { badFields[globals.VersionField] = "Required field." } - if len(req.GetApplicationCredentialSourceIds()) == 0 { - badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty." + if len(req.GetApplicationCredentialSourceIds())+len(req.GetEgressCredentialSourceIds()) == 0 { + badFields[globals.ApplicationCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." + badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." } for _, cl := range req.GetApplicationCredentialSourceIds() { if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { @@ -1969,6 +1999,12 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc break } } + for _, cl := range req.GetEgressCredentialSourceIds() { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) + break + } + } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) } @@ -2015,3 +2051,23 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error { } return nil } + +func createCredLibs(targetId string, applicationIds, ingressIds, egressIds []string) ([]*target.CredentialLibrary, error) { + credLibs := make([]*target.CredentialLibrary, 0, len(applicationIds)+len(ingressIds)+len(egressIds)) + + byPurpose := map[credential.Purpose][]string{ + credential.ApplicationPurpose: strutil.RemoveDuplicates(applicationIds, false), + credential.IngressPurpose: strutil.RemoveDuplicates(ingressIds, false), + credential.EgressPurpose: strutil.RemoveDuplicates(egressIds, false), + } + for purpose, ids := range byPurpose { + for _, id := range ids { + l, err := target.NewCredentialLibrary(targetId, id, purpose) + if err != nil { + return nil, err + } + credLibs = append(credLibs, l) + } + } + return credLibs, nil +} diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index a7d51b883..33c278252 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -14,6 +14,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/authtoken" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" @@ -293,7 +294,16 @@ func TestList(t *testing.T) { return } require.NoError(gErr) - assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListTargets(%q) scope %q, got response %q, wanted %q", tc.name, tc.req.GetScopeId(), got, tc.res) + assert.Equal(len(tc.res.Items), len(got.Items)) + wantById := make(map[string]*pb.Target, len(tc.res.Items)) + for _, t := range tc.res.Items { + wantById[t.Id] = t + } + for _, t := range got.Items { + want, ok := wantById[t.Id] + assert.True(ok, "Got unexpected target with id: %s", t.Id) + assert.Empty(cmp.Diff(t, want, protocmp.Transform()), "got %v, wanted %v", t, want) + } // Test with anon user got, gErr = s.ListTargets(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req) @@ -1806,13 +1816,13 @@ func TestAddTargetLibraries(t *testing.T) { }, { name: "Add library on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for libraries", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add duplicated libraries on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for libraries", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, @@ -1942,19 +1952,19 @@ func TestSetTargetLibraries(t *testing.T) { }, { name: "Set on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Set duplicate libraries on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setLibraries: []string{}, resultLibraryIds: nil, }, @@ -2068,13 +2078,13 @@ func TestRemoveTargetLibraries(t *testing.T) { }, { name: "Remove 1 of 2 libraries", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeLibs: []string{cls[1].GetPublicId()}, resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 libraries", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeLibs: []string{ cls[1].GetPublicId(), cls[1].GetPublicId(), }, @@ -2082,7 +2092,7 @@ func TestRemoveTargetLibraries(t *testing.T) { }, { name: "Remove all libraries from target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeLibs: []string{ cls[0].GetPublicId(), cls[1].GetPublicId(), }, @@ -2203,13 +2213,13 @@ func TestAddTargetSources(t *testing.T) { }, { name: "Add source on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add duplicated sources on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, @@ -2339,19 +2349,19 @@ func TestSetTargetCredentialSources(t *testing.T) { }, { name: "Set on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Set duplicate sources on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{}, resultCredentialSourceIds: nil, }, @@ -2465,13 +2475,13 @@ func TestRemoveTargetCredentialSources(t *testing.T) { }, { name: "Remove 1 of 2 sources", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 sources", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeCredentialSources: []string{ cls[1].GetPublicId(), cls[1].GetPublicId(), }, @@ -2479,7 +2489,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) { }, { name: "Remove all sources from target", - tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), removeCredentialSources: []string{ cls[0].GetPublicId(), cls[1].GetPublicId(), }, diff --git a/internal/session/immutable_fields_test.go b/internal/session/immutable_fields_test.go index ec90235a1..96c7315e4 100644 --- a/internal/session/immutable_fields_test.go +++ b/internal/session/immutable_fields_test.go @@ -6,10 +6,12 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" + "gorm.io/gorm/logger" ) func TestSession_ImmutableFields(t *testing.T) { @@ -245,16 +247,20 @@ func TestConnectionState_ImmutableFields(t *testing.T) { _, _ = iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) session := TestDefaultSession(t, conn, wrapper, iamRepo) connection := TestConnection(t, conn, session.PublicId, "127.0.0.1", 22, "127.0.0.1", 2222) - state := TestConnectionState(t, conn, connection.PublicId, StatusClosed) + state := TestConnectionState(t, conn, connection.PublicId, StatusConnected) var new ConnectionState err := rw.LookupWhere(context.Background(), &new, "connection_id = ? and state = ?", state.ConnectionId, state.Status) require.NoError(t, err) + conn.Logger = logger.Default.LogMode(logger.Info) + tests := []struct { - name string - update *ConnectionState - fieldMask []string + name string + update *ConnectionState + fieldMask []string + wantErrMatch *errors.Template + wantErrContains string }{ { name: "session_id", @@ -272,7 +278,9 @@ func TestConnectionState_ImmutableFields(t *testing.T) { s.Status = "closed" return s }(), - fieldMask: []string{"Status"}, + fieldMask: []string{"Status"}, + wantErrMatch: errors.T(errors.NotSpecificIntegrity), + wantErrContains: "immutable column", }, { name: "start time", @@ -281,7 +289,9 @@ func TestConnectionState_ImmutableFields(t *testing.T) { s.StartTime = &ts return s }(), - fieldMask: []string{"StartTime"}, + fieldMask: []string{"StartTime"}, + wantErrMatch: errors.T(errors.InvalidFieldMask), + wantErrContains: "parameter violation", }, { name: "previous_end_time", @@ -290,7 +300,9 @@ func TestConnectionState_ImmutableFields(t *testing.T) { s.PreviousEndTime = &ts return s }(), - fieldMask: []string{"PreviousEndTime"}, + fieldMask: []string{"PreviousEndTime"}, + wantErrMatch: errors.T(errors.NotSpecificIntegrity), + wantErrContains: "immutable column", }, } for _, tt := range tests { @@ -304,7 +316,12 @@ func TestConnectionState_ImmutableFields(t *testing.T) { rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) require.Error(err) assert.Equal(0, rowsUpdated) - + if tt.wantErrMatch != nil { + assert.Truef(errors.Match(tt.wantErrMatch, err), "wanted error %s and got: %s", tt.wantErrMatch.Code, err.Error()) + } + if tt.wantErrContains != "" { + assert.Contains(err.Error(), tt.wantErrContains) + } after := new.Clone() err = rw.LookupWhere(context.Background(), after, "connection_id = ? and start_time = ?", new.ConnectionId, new.StartTime) require.NoError(err) diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 68e9076f1..1bc59ee9d 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -1649,21 +1649,26 @@ func testSessionCredentialParams(t *testing.T, conn *db.DB, wrapper wrapping.Wra rw := db.New(conn) ctx := context.Background() - stores := vault.TestCredentialStores(t, conn, wrapper, params.ScopeId, 1) - libs := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) kms := kms.TestKms(t, conn, wrapper) targetRepo, err := target.NewRepository(rw, rw, kms) require.NoError(err) - tcpTarget, _, _, err := targetRepo.LookupTarget(ctx, params.TargetId) + tar, _, _, err := targetRepo.LookupTarget(ctx, params.TargetId) require.NoError(err) - require.NotNil(tcpTarget) - _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tcpTarget.GetPublicId(), tcpTarget.GetVersion(), []string{libs[0].PublicId, libs[1].PublicId}) + require.NotNil(tar) + + stores := vault.TestCredentialStores(t, conn, wrapper, params.ScopeId, 1) + libIds := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) + libs := []*target.CredentialLibrary{ + target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[1].GetPublicId(), credential.ApplicationPurpose), + } + + _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), libs) require.NoError(err) creds := []*DynamicCredential{ - NewDynamicCredential(libs[0].GetPublicId(), credential.ApplicationPurpose), - NewDynamicCredential(libs[0].GetPublicId(), credential.IngressPurpose), - NewDynamicCredential(libs[1].GetPublicId(), credential.EgressPurpose), + NewDynamicCredential(libIds[0].GetPublicId(), credential.ApplicationPurpose), + NewDynamicCredential(libIds[1].GetPublicId(), credential.ApplicationPurpose), } params.DynamicCredentials = creds return params diff --git a/internal/session/util.go b/internal/session/util.go index bbdbe6316..f94f98dd9 100644 --- a/internal/session/util.go +++ b/internal/session/util.go @@ -4,7 +4,7 @@ import ( "crypto/ed25519" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" wrapping "github.com/hashicorp/go-kms-wrapping" ) @@ -19,8 +19,11 @@ func DeriveED25519Key(wrapper wrapping.Wrapper, userId, jobId string) (ed25519.P if jobId != "" { jId = []byte(jobId) } + if wrapper == nil { + return nil, nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing wrapper") + } - reader, err := kms.NewDerivedReader(wrapper, 32, uId, jId) + reader, err := crypto.NewDerivedReader(wrapper, 32, uId, jId) if err != nil { return nil, nil, errors.WrapDeprecated(err, op) } diff --git a/internal/session/util_test.go b/internal/session/util_test.go index 78203ec14..09e8eee1c 100644 --- a/internal/session/util_test.go +++ b/internal/session/util_test.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -39,7 +39,7 @@ func TestDeriveED25519Key(t *testing.T) { jobId: "jobId", }, want: func() keys { - reader, err := kms.NewDerivedReader(wrapper, 32, []byte("userId"), []byte("jobId")) + reader, err := crypto.NewDerivedReader(wrapper, 32, []byte("userId"), []byte("jobId")) require.NoError(t, err) pub, priv, err := ed25519.GenerateKey(reader) require.NoError(t, err) diff --git a/internal/target/credential_library.go b/internal/target/credential_library.go index 97d83f811..8c013a4d8 100644 --- a/internal/target/credential_library.go +++ b/internal/target/credential_library.go @@ -21,7 +21,7 @@ type CredentialLibrary struct { // NewCredentialLibrary creates a new in memory CredentialLibrary // representing the relationship between targetId and credentialLibraryId. -func NewCredentialLibrary(targetId, credentialLibraryId string, _ ...Option) (*CredentialLibrary, error) { +func NewCredentialLibrary(targetId, credentialLibraryId string, purpose credential.Purpose) (*CredentialLibrary, error) { const op = "target.NewCredentialLibrary" if targetId == "" { return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no target id") @@ -34,7 +34,7 @@ func NewCredentialLibrary(targetId, credentialLibraryId string, _ ...Option) (*C CredentialLibrary: &store.CredentialLibrary{ TargetId: targetId, CredentialLibraryId: credentialLibraryId, - CredentialPurpose: string(credential.ApplicationPurpose), // application is the only purpose currently supported + CredentialPurpose: string(purpose), }, } return t, nil diff --git a/internal/target/credential_library_test.go b/internal/target/credential_library_test.go index a079f471b..fb36b27f9 100644 --- a/internal/target/credential_library_test.go +++ b/internal/target/credential_library_test.go @@ -55,7 +55,7 @@ func TestCredentialLibrary_New(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - got, err := target.NewCredentialLibrary(tt.args.targetId, tt.args.libraryId) + got, err := target.NewCredentialLibrary(tt.args.targetId, tt.args.libraryId, credential.ApplicationPurpose) if tt.wantErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) assert.Nil(got) diff --git a/internal/target/options.go b/internal/target/options.go index 5fa1bbe08..69565cd3c 100644 --- a/internal/target/options.go +++ b/internal/target/options.go @@ -30,7 +30,7 @@ type options struct { WithUserId string WithType subtypes.Subtype WithHostSources []string - WithCredentialSources []string + WithCredentialLibraries []*CredentialLibrary WithSessionMaxSeconds uint32 WithSessionConnectionLimit int32 WithPublicId string @@ -49,7 +49,7 @@ func getDefaultOptions() options { WithUserId: "", WithType: "", WithHostSources: nil, - WithCredentialSources: nil, + WithCredentialLibraries: nil, WithSessionMaxSeconds: uint32((8 * time.Hour).Seconds()), WithSessionConnectionLimit: 1, WithPublicId: "", @@ -129,10 +129,10 @@ func WithHostSources(hs []string) Option { } } -// WithCredentialSources provides an option for providing a list of credential source ids -func WithCredentialSources(cl []string) Option { +// WithCredentialLibraries provides an option for providing a list of credential libraries. +func WithCredentialLibraries(cl []*CredentialLibrary) Option { return func(o *options) { - o.WithCredentialSources = cl + o.WithCredentialLibraries = cl } } diff --git a/internal/target/options_test.go b/internal/target/options_test.go index a4d3431f5..0746102d6 100644 --- a/internal/target/options_test.go +++ b/internal/target/options_test.go @@ -3,6 +3,8 @@ package target import ( "testing" + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/target/store" "github.com/hashicorp/boundary/internal/types/subtypes" "github.com/stretchr/testify/assert" ) @@ -106,11 +108,37 @@ func Test_GetOpts(t *testing.T) { testOpts.WithWorkerFilter = `"/foo" == "bar"` assert.Equal(opts, testOpts) }) - t.Run("WithCredentialSources", func(t *testing.T) { + t.Run("WithCredentialLibraries", func(t *testing.T) { assert := assert.New(t) - opts := GetOpts(WithCredentialSources([]string{"alice", "bob"})) + opts := GetOpts(WithCredentialLibraries([]*CredentialLibrary{ + { + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: "alice", + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + { + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: "bob", + CredentialPurpose: string(credential.EgressPurpose), + }, + }, + })) testOpts := getDefaultOptions() - testOpts.WithCredentialSources = []string{"alice", "bob"} + testOpts.WithCredentialLibraries = []*CredentialLibrary{ + { + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: "alice", + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + { + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: "bob", + CredentialPurpose: string(credential.EgressPurpose), + }, + }, + } assert.Equal(opts, testOpts) }) } diff --git a/internal/target/query.go b/internal/target/query.go index 7dc5c09a4..054861eee 100644 --- a/internal/target/query.go +++ b/internal/target/query.go @@ -13,7 +13,8 @@ current_libraries (library_id) as ( -- returns the current list select credential_library_id from target_credential_library - where target_id = @target_id + where target_id = @target_id + and credential_purpose = @purpose ), keep_libraries (library_id) as ( -- returns the KEEP list diff --git a/internal/target/registry.go b/internal/target/registry.go index d33f3035c..333cdd725 100644 --- a/internal/target/registry.go +++ b/internal/target/registry.go @@ -15,10 +15,15 @@ type AllocFunc func() Target // be used by the Repository. type VetFunc func(context.Context, Target) error +// VetCredentialLibrariesFunc is a function that checks the given CredentialLibraries +// to ensure that they are valid for a Target subtype. +type VetCredentialLibrariesFunc func(context.Context, []*CredentialLibrary) error + type registryEntry struct { - alloc AllocFunc - vet VetFunc - prefix string + alloc AllocFunc + vet VetFunc + vetCredentialLibraries VetCredentialLibrariesFunc + prefix string } type registry struct { @@ -71,6 +76,14 @@ func (r *registry) vetFunc(s subtypes.Subtype) (VetFunc, bool) { return entry.vet, ok } +func (r *registry) vetCredentialLibrariesFunc(s subtypes.Subtype) (VetCredentialLibrariesFunc, bool) { + entry, ok := r.get(s) + if !ok { + return nil, ok + } + return entry.vetCredentialLibraries, ok +} + func (r *registry) idPrefix(s subtypes.Subtype) (string, bool) { entry, ok := r.get(s) if !ok { @@ -103,10 +116,11 @@ func SubtypeFromId(id string) subtypes.Subtype { // Register registers repository hooks and the prefixes for a provided Subtype. Register // panics if the subtype has already been registered or if any of the // prefixes are associated with another subtype. -func Register(s subtypes.Subtype, af AllocFunc, vf VetFunc, prefix string) { +func Register(s subtypes.Subtype, af AllocFunc, vf VetFunc, vclf VetCredentialLibrariesFunc, prefix string) { subtypeRegistry.set(s, ®istryEntry{ - alloc: af, - vet: vf, - prefix: prefix, + alloc: af, + vet: vf, + vetCredentialLibraries: vclf, + prefix: prefix, }) } diff --git a/internal/target/repository_credential_source.go b/internal/target/repository_credential_source.go index 31284bf1e..8d158b885 100644 --- a/internal/target/repository_credential_source.go +++ b/internal/target/repository_credential_source.go @@ -6,17 +6,46 @@ import ( "fmt" "strings" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target/store" ) +// credentialLibrariesPurposeMap is used to group CredentialLibraries by purpose. +type credentialLibrariesPurposeMap map[string][]*CredentialLibrary + +func (c credentialLibrariesPurposeMap) add(cl *CredentialLibrary) { + var s []*CredentialLibrary + var ok bool + + s, ok = c[cl.CredentialPurpose] + if !ok { + s = make([]*CredentialLibrary, 0, 1) + } + + s = append(s, cl) + c[cl.CredentialPurpose] = s +} + +func newCredentialLibrariesPurposeMap(cls []*CredentialLibrary) credentialLibrariesPurposeMap { + clpm := make(credentialLibrariesPurposeMap) + for _, p := range credential.ValidPurposes { + clpm[p.String()] = make([]*CredentialLibrary, 0) + } + for _, cl := range cls { + clpm.add(cl) + } + return clpm +} + // AddTargetCredentialSources adds the cIds to the targetId in the repository. The target // and the list of credential sources attached to the target, after cIds are added, // will be returned on success. // The targetVersion must match the current version of the targetId in the repository. -func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cIds []string, _ ...Option) (Target, []HostSource, []CredentialSource, error) { +func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) (Target, []HostSource, []CredentialSource, error) { const op = "target.(Repository).AddTargetCredentialSources" if targetId == "" { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -24,16 +53,12 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st if targetVersion == 0 { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if len(cIds) == 0 { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential source ids") + if len(cls) == 0 { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential libraries") } - addCredLibs := make([]interface{}, 0, len(cIds)) - for _, id := range cIds { - cl, err := NewCredentialLibrary(targetId, id) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory credential library")) - } + addCredLibs := make([]interface{}, 0, len(cls)) + for _, cl := range cls { addCredLibs = append(addCredLibs, cl) } @@ -49,6 +74,14 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + vetCredentialLibraries, ok := subtypeRegistry.vetCredentialLibrariesFunc(t.Subtype()) + if !ok { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) + } + if err := vetCredentialLibraries(ctx, cls); err != nil { + return nil, nil, nil, err + } + target := alloc() target.SetPublicId(ctx, t.PublicId) target.SetVersion(targetVersion + 1) @@ -115,7 +148,7 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st // DeleteTargetCredentialSources deletes credential sources from a target in the repository. // The target's current db version must match the targetVersion or an error will be returned. -func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, csIds []string, _ ...Option) (int, error) { +func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) (int, error) { const op = "target.(Repository).DeleteTargetCredentialSources" if targetId == "" { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -123,16 +156,12 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId if targetVersion == 0 { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if len(csIds) == 0 { + if len(cls) == 0 { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing credential source ids") } - deleteCredLibs := make([]interface{}, 0, len(csIds)) - for _, id := range csIds { - cl, err := NewCredentialLibrary(targetId, id) - if err != nil { - return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory credential library")) - } + deleteCredLibs := make([]interface{}, 0, len(cls)) + for _, cl := range cls { deleteCredLibs = append(deleteCredLibs, cl) } @@ -208,7 +237,7 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId // SetTargetCredentialSources will set the target's credential sources. Set will add // and/or delete credential sources as need to reconcile the existing credential sources // with the request. If clIds is empty, all the credential sources will be cleared from the target. -func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, csIds []string, _ ...Option) ([]HostSource, []CredentialSource, int, error) { +func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) ([]HostSource, []CredentialSource, int, error) { const op = "target.(Repository).SetTargetCredentialSources" if targetId == "" { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -217,10 +246,18 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - changes, err := r.changes(ctx, targetId, csIds) - if err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + clpm := newCredentialLibrariesPurposeMap(cls) + + var changes []*change + + for purpose, cls := range clpm { + c, err := r.changes(ctx, targetId, cls, purpose) + if err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + changes = append(changes, c...) } + if len(changes) == 0 { // Nothing needs to be changed, return early hostSets, err := fetchHostSources(ctx, r.reader, targetId) @@ -236,15 +273,11 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st var deleteCredLibs, addCredLibs []interface{} for _, c := range changes { - cl, err := NewCredentialLibrary(targetId, c.LibraryId) - if err != nil { - return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory target credential library")) - } - switch c.Action { + switch c.action { case "delete": - deleteCredLibs = append(deleteCredLibs, cl) + deleteCredLibs = append(deleteCredLibs, c.credentialLibrary) case "add": - addCredLibs = append(addCredLibs, cl) + addCredLibs = append(addCredLibs, c.credentialLibrary) } } @@ -259,6 +292,15 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st if !ok { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + + vetCredentialLibraries, ok := subtypeRegistry.vetCredentialLibrariesFunc(t.Subtype()) + if !ok { + return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) + } + if err := vetCredentialLibraries(ctx, cls); err != nil { + return nil, nil, db.NoRowsAffected, err + } + target := alloc() target.SetPublicId(ctx, t.PublicId) target.SetVersion(targetVersion + 1) @@ -343,28 +385,33 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st } type change struct { + action string + credentialLibrary *CredentialLibrary +} + +type changeQueryResult struct { Action string LibraryId string } -func (r *Repository) changes(ctx context.Context, targetId string, clIds []string) ([]*change, error) { +func (r *Repository) changes(ctx context.Context, targetId string, cls []*CredentialLibrary, purpose string) ([]*change, error) { const op = "target.(Repository).changes" + + // TODO ensure that all cls have the same purpose as the given purpose? + var inClauseSpots []string - // starts at 2 because there is already a @1 in the query - for i := 2; i < len(clIds)+2; i++ { - inClauseSpots = append(inClauseSpots, fmt.Sprintf("@%d", i)) + var params []interface{} + params = append(params, sql.Named("target_id", targetId), sql.Named("purpose", purpose)) + for idx, cl := range cls { + params = append(params, sql.Named(fmt.Sprintf("%d", idx+1), cl.CredentialLibraryId)) + inClauseSpots = append(inClauseSpots, fmt.Sprintf("@%d", idx+1)) } inClause := strings.Join(inClauseSpots, ",") if inClause == "" { inClause = "''" } - query := fmt.Sprintf(setChangesQuery, inClause) - var params []interface{} - params = append(params, sql.Named("target_id", targetId)) - for idx, id := range clIds { - params = append(params, sql.Named(fmt.Sprintf("%d", idx+2), id)) - } + query := fmt.Sprintf(setChangesQuery, inClause) rows, err := r.reader.Query(ctx, query, params) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("query failed")) @@ -373,11 +420,20 @@ func (r *Repository) changes(ctx context.Context, targetId string, clIds []strin var changes []*change for rows.Next() { - var chg change + var chg changeQueryResult if err := r.reader.ScanRows(rows, &chg); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed")) } - changes = append(changes, &chg) + changes = append(changes, &change{ + action: chg.Action, + credentialLibrary: &CredentialLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + TargetId: targetId, + CredentialLibraryId: chg.LibraryId, + CredentialPurpose: purpose, + }, + }, + }) } return changes, nil } diff --git a/internal/target/repository_credential_source_test.go b/internal/target/repository_credential_source_test.go new file mode 100644 index 000000000..d03c438f8 --- /dev/null +++ b/internal/target/repository_credential_source_test.go @@ -0,0 +1,258 @@ +package target_test + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/vault" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/targettest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_SetTargetCredentialSources(t *testing.T) { + target.Register(targettest.Subtype, targettest.Alloc, targettest.Vet, targettest.VetCredentialLibraries, targettest.TargetPrefix) + + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(t, err) + + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + + cs := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2) + lib1 := credLibs[0] + lib2 := credLibs[1] + + setupFn := func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 10) + cls := make([]*target.CredentialLibrary, 0, len(credLibs)) + for _, cl := range credLibs { + cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) + } + + _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) + require.NoError(t, err) + require.Equal(t, 10, len(created)) + return created, cls + } + type args struct { + targetVersion uint32 + cls []*target.CredentialLibrary + addToOrigLibs bool + } + tests := []struct { + name string + setup func(target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) + args args + wantAffectedRows int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "clear", + setup: setupFn, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{}, + }, + wantErr: false, + wantAffectedRows: 10, + }, + { + name: "no-change", + setup: setupFn, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{}, + addToOrigLibs: true, + }, + wantErr: false, + wantAffectedRows: 0, + }, + { + name: "add-cred-sources", + setup: setupFn, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, + addToOrigLibs: true, + }, + wantErr: false, + wantAffectedRows: 2, + }, + { + name: "add-multiple-purposes", + setup: setupFn, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, + addToOrigLibs: true, + }, + wantErr: false, + wantAffectedRows: 3, + }, + { + name: "remove-add-change-purpose", + setup: func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { + cls := []*target.CredentialLibrary{ + target.TestNewCredentialLibrary(tar.GetPublicId(), lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary(tar.GetPublicId(), lib2.PublicId, credential.ApplicationPurpose), + } + + _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) + require.NoError(t, err) + require.Equal(t, 2, len(created)) + return created, cls + }, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.EgressPurpose), + }, + addToOrigLibs: false, + }, + wantErr: false, + wantAffectedRows: 4, + }, + { + name: "zero version", + setup: setupFn, + args: args{ + targetVersion: 0, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, + addToOrigLibs: true, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "bad version", + setup: setupFn, + args: args{ + targetVersion: 1000, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, + addToOrigLibs: true, + }, + wantErr: true, + wantErrCode: errors.VersionMismatch, + }, + { + name: "remove existing and add cred libs", + setup: setupFn, + args: args{ + targetVersion: 2, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, + addToOrigLibs: false, + }, + wantErr: false, + wantAffectedRows: 12, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + tar := targettest.TestNewTestTarget(t, conn, proj.PublicId, tt.name) + + var origCredSources []target.CredentialSource + var origCredLibraries []*target.CredentialLibrary + if tt.setup != nil { + origCredSources, origCredLibraries = tt.setup(tar) + } + + wantCredSources := make(map[string]target.CredentialSource) + for _, cl := range tt.args.cls { + cl.TargetId = tar.GetPublicId() + wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ + CredentialLibrary: cl.CredentialLibrary, + } + } + if tt.args.addToOrigLibs { + tt.args.cls = append(tt.args.cls, origCredLibraries...) + for _, cl := range origCredLibraries { + wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ + CredentialLibrary: cl.CredentialLibrary, + } + } + } + + origTarget, _, lookupCredSources, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) + require.NoError(err) + assert.Equal(origCredSources, lookupCredSources) + + _, got, affectedRows, err := repo.SetTargetCredentialSources(context.Background(), tar.GetPublicId(), tt.args.targetVersion, tt.args.cls) + if tt.wantErr { + require.Error(err) + assert.Equal(0, affectedRows) + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "unexpected error %s", err.Error()) + return + } + t.Log(err) + require.NoError(err) + assert.Equal(tt.wantAffectedRows, affectedRows) + + assert.Equal(len(wantCredSources), len(got)) + + for _, cs := range got { + w, ok := wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] + assert.True(ok, "got unexpected credentialsource %v", cs) + assert.Equal(w.Id(), cs.Id()) + assert.Equal(w.CredentialPurpose(), cs.CredentialPurpose()) + } + + foundTarget, _, _, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) + require.NoError(err) + if tt.name != "no-change" { + assert.Equalf(tt.args.targetVersion+1, foundTarget.GetVersion(), "%s unexpected version: %d/%d", tt.name, tt.args.targetVersion+1, foundTarget.GetVersion()) + assert.Equalf(origTarget.GetVersion(), foundTarget.GetVersion()-1, "%s unexpected version: %d/%d", tt.name, origTarget.GetVersion(), foundTarget.GetVersion()-1) + } + }) + } + t.Run("missing-target-id", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + cl1 := target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, []*target.CredentialLibrary{cl1}) + + require.Error(err) + assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) + }) + t.Run("target-not-found", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) + + require.Error(err) + assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) + }) +} diff --git a/internal/target/targettest/store/target.pb.go b/internal/target/targettest/store/target.pb.go new file mode 100644 index 000000000..cb4e299fc --- /dev/null +++ b/internal/target/targettest/store/target.pb.go @@ -0,0 +1,307 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: controller/storage/target/targettest/store/v1/target.proto + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Target struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is used to access the targettest.Target via an API + // @inject_tag: gorm:"primary_key" + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // scope id for the targettest.Target + // @inject_tag: `gorm:"default:null"` + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"` + // name is the optional friendly name used to + // access the targettest.Target via an API + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description of the targettest.Target + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // update_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // version allows optimistic locking of the targettest.Target when modifying the + // targettest.Target + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // default port of the targettest.Target + // @inject_tag: `gorm:"default:null"` + DefaultPort uint32 `protobuf:"varint,80,opt,name=default_port,json=defaultPort,proto3" json:"default_port,omitempty" gorm:"default:null"` + // Maximum total lifetime of a created session, in seconds + // @inject_tag: `gorm:"default:null"` + SessionMaxSeconds uint32 `protobuf:"varint,100,opt,name=session_max_seconds,json=sessionMaxSeconds,proto3" json:"session_max_seconds,omitempty" gorm:"default:null"` + // Maximum number of connections in a session + // @inject_tag: `gorm:"default:null"` + SessionConnectionLimit int32 `protobuf:"varint,110,opt,name=session_connection_limit,json=sessionConnectionLimit,proto3" json:"session_connection_limit,omitempty" gorm:"default:null"` + // A boolean expression that allows filtering the workers that can handle a session + // @inject_tag: `gorm:"default:null"` + WorkerFilter string `protobuf:"bytes,120,opt,name=worker_filter,json=workerFilter,proto3" json:"worker_filter,omitempty" gorm:"default:null"` +} + +func (x *Target) Reset() { + *x = Target{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_target_targettest_store_v1_target_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Target) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Target) ProtoMessage() {} + +func (x *Target) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_target_targettest_store_v1_target_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Target.ProtoReflect.Descriptor instead. +func (*Target) Descriptor() ([]byte, []int) { + return file_controller_storage_target_targettest_store_v1_target_proto_rawDescGZIP(), []int{0} +} + +func (x *Target) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Target) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *Target) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Target) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Target) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Target) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Target) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Target) GetDefaultPort() uint32 { + if x != nil { + return x.DefaultPort + } + return 0 +} + +func (x *Target) GetSessionMaxSeconds() uint32 { + if x != nil { + return x.SessionMaxSeconds + } + return 0 +} + +func (x *Target) GetSessionConnectionLimit() int32 { + if x != nil { + return x.SessionConnectionLimit + } + return 0 +} + +func (x *Target) GetWorkerFilter() string { + if x != nil { + return x.WorkerFilter + } + return "" +} + +var File_controller_storage_target_targettest_store_v1_target_proto protoreflect.FileDescriptor + +var file_controller_storage_target_targettest_store_v1_target_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2d, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x05, 0x0a, 0x06, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, + 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x2a, 0xc2, + 0xdd, 0x29, 0x26, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x5c, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x64, 0x20, + 0x01, 0x28, 0x0d, 0x42, 0x2c, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x11, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x52, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x70, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x6e, 0x20, 0x01, 0x28, 0x05, 0x42, 0x36, 0xc2, 0xdd, 0x29, 0x32, 0x0a, 0x16, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x16, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x46, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xc2, + 0xdd, 0x29, 0x1d, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x12, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x46, + 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_target_targettest_store_v1_target_proto_rawDescOnce sync.Once + file_controller_storage_target_targettest_store_v1_target_proto_rawDescData = file_controller_storage_target_targettest_store_v1_target_proto_rawDesc +) + +func file_controller_storage_target_targettest_store_v1_target_proto_rawDescGZIP() []byte { + file_controller_storage_target_targettest_store_v1_target_proto_rawDescOnce.Do(func() { + file_controller_storage_target_targettest_store_v1_target_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_target_targettest_store_v1_target_proto_rawDescData) + }) + return file_controller_storage_target_targettest_store_v1_target_proto_rawDescData +} + +var file_controller_storage_target_targettest_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_target_targettest_store_v1_target_proto_goTypes = []interface{}{ + (*Target)(nil), // 0: controller.storage.target.targettest.store.v1.Target + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_target_targettest_store_v1_target_proto_depIdxs = []int32{ + 1, // 0: controller.storage.target.targettest.store.v1.Target.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.target.targettest.store.v1.Target.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_storage_target_targettest_store_v1_target_proto_init() } +func file_controller_storage_target_targettest_store_v1_target_proto_init() { + if File_controller_storage_target_targettest_store_v1_target_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_target_targettest_store_v1_target_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Target); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_target_targettest_store_v1_target_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_target_targettest_store_v1_target_proto_goTypes, + DependencyIndexes: file_controller_storage_target_targettest_store_v1_target_proto_depIdxs, + MessageInfos: file_controller_storage_target_targettest_store_v1_target_proto_msgTypes, + }.Build() + File_controller_storage_target_targettest_store_v1_target_proto = out.File + file_controller_storage_target_targettest_store_v1_target_proto_rawDesc = nil + file_controller_storage_target_targettest_store_v1_target_proto_goTypes = nil + file_controller_storage_target_targettest_store_v1_target_proto_depIdxs = nil +} diff --git a/internal/target/targettest/target.go b/internal/target/targettest/target.go new file mode 100644 index 000000000..4d9b3e31b --- /dev/null +++ b/internal/target/targettest/target.go @@ -0,0 +1,267 @@ +// Package targettest provides a test target subtype for use by the target +// package. Note that it leverages the tcp.Target's database table to avoid +// needing schema migrations just for tests. +package targettest + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/targettest/store" + "github.com/hashicorp/boundary/internal/types/subtypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +const ( + Subtype = subtypes.Subtype("tcp") + TargetPrefix = "ttcp" +) + +// Target is a target.Target used for tests. +type Target struct { + *store.Target + tableName string `gorm:"-"` +} + +var ( + _ target.Target = (*Target)(nil) + _ db.VetForWriter = (*Target)(nil) + _ oplog.ReplayableMessage = (*Target)(nil) +) + +// VetForWrite implements db.VetForWrite() interface and validates the tcp target +// before it's written. +func (t *Target) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error { + const op = "target_test.(Target).VetForWrite" + if t.PublicId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing public id") + } + if opType == db.CreateOp { + if t.ScopeId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if t.Name == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing name") + } + } + return nil +} + +// TableName returns the tablename to override the default gorm table name +func (t *Target) TableName() string { + if t.tableName != "" { + return t.tableName + } + return "target_tcp" +} + +// SetTableName sets the tablename and satisfies the ReplayableMessage +// interface. If the caller attempts to set the name to "" the name will be +// reset to the default name. +func (t *Target) SetTableName(n string) { + t.tableName = n +} + +func (t *Target) GetPublicId() string { + return t.PublicId +} + +func (t *Target) GetScopeId() string { + return t.ScopeId +} + +func (t *Target) GetDefaultPort() uint32 { + return t.DefaultPort +} + +func (t *Target) GetName() string { + return t.Name +} + +func (t *Target) GetDescription() string { + return t.Description +} + +func (t *Target) GetVersion() uint32 { + return t.Version +} + +func (t *Target) GetType() subtypes.Subtype { + return Subtype +} + +func (t *Target) GetCreateTime() *timestamp.Timestamp { + return t.CreateTime +} + +func (t *Target) GetUpdateTime() *timestamp.Timestamp { + return t.UpdateTime +} + +func (t *Target) GetSessionMaxSeconds() uint32 { + return t.SessionMaxSeconds +} + +func (t *Target) GetSessionConnectionLimit() int32 { + return t.SessionConnectionLimit +} + +func (t *Target) GetWorkerFilter() string { + return t.WorkerFilter +} + +func (t *Target) Clone() target.Target { + cp := proto.Clone(t.Target) + return &Target{ + Target: cp.(*store.Target), + } +} + +func (t *Target) SetPublicId(_ context.Context, publicId string) error { + t.PublicId = publicId + return nil +} + +func (t *Target) SetScopeId(scopeId string) { + t.ScopeId = scopeId +} + +func (t *Target) SetName(name string) { + t.Name = name +} + +func (t *Target) SetDescription(description string) { + t.Description = description +} + +func (t *Target) SetVersion(v uint32) { + t.Version = v +} + +func (t *Target) SetDefaultPort(port uint32) { + t.DefaultPort = port +} + +func (t *Target) SetCreateTime(ts *timestamp.Timestamp) { + t.CreateTime = ts +} + +func (t *Target) SetUpdateTime(ts *timestamp.Timestamp) { + t.UpdateTime = ts +} + +func (t *Target) SetSessionMaxSeconds(s uint32) { + t.SessionMaxSeconds = s +} + +func (t *Target) SetSessionConnectionLimit(l int32) { + t.SessionConnectionLimit = l +} + +func (t *Target) SetWorkerFilter(f string) { + t.WorkerFilter = f +} + +func (t *Target) Oplog(op oplog.OpType) oplog.Metadata { + return oplog.Metadata{ + "resource-public-id": []string{t.PublicId}, + "resource-type": []string{"tcp target"}, + "op-type": []string{op.String()}, + "scope-id": []string{t.ScopeId}, + } +} + +// Alloc creates an in-memory Target. +func Alloc() target.Target { + return &Target{ + Target: &store.Target{}, + } +} + +// Vet checks that the given Target is a targettest.Target and that it is not nil. +func Vet(ctx context.Context, t target.Target) error { + const op = "targettest.Vet" + + tt, ok := t.(*Target) + if !ok { + return errors.New(ctx, errors.InvalidParameter, op, "target is not a tcp.Target") + } + + if tt == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing target") + } + + if tt.Target == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing target store") + } + return nil +} + +// VetCredentialLibraries allows for any CredentialLibraries. +func VetCredentialLibraries(_ context.Context, _ []*target.CredentialLibrary) error { + return nil +} + +// New creates a targettest.Target. +func New(scopeId string, opt ...target.Option) (*Target, error) { + const op = "target_test.New" + opts := target.GetOpts(opt...) + if scopeId == "" { + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing scope id") + } + t := &Target{ + Target: &store.Target{ + ScopeId: scopeId, + Name: opts.WithName, + Description: opts.WithDescription, + DefaultPort: opts.WithDefaultPort, + SessionConnectionLimit: opts.WithSessionConnectionLimit, + SessionMaxSeconds: opts.WithSessionMaxSeconds, + WorkerFilter: opts.WithWorkerFilter, + }, + } + return t, nil +} + +// TestNewTestTarget is a test helper for creating a targettest.Target. +func TestNewTestTarget(t *testing.T, conn *db.DB, scopeId, name string, opt ...target.Option) *Target { + t.Helper() + opt = append(opt, target.WithName(name)) + opts := target.GetOpts(opt...) + require := require.New(t) + rw := db.New(conn) + tar, err := New(scopeId, opt...) + require.NoError(err) + id, err := db.NewPublicId(TargetPrefix) + require.NoError(err) + tar.PublicId = id + err = rw.Create(context.Background(), tar) + require.NoError(err) + + if len(opts.WithHostSources) > 0 { + newHostSets := make([]interface{}, 0, len(opts.WithHostSources)) + for _, s := range opts.WithHostSources { + hostSet, err := target.NewTargetHostSet(tar.PublicId, s) + require.NoError(err) + newHostSets = append(newHostSets, hostSet) + } + err := rw.CreateItems(context.Background(), newHostSets) + require.NoError(err) + } + if len(opts.WithCredentialLibraries) > 0 { + newCredLibs := make([]interface{}, 0, len(opts.WithCredentialLibraries)) + for _, cl := range opts.WithCredentialLibraries { + cl.TargetId = tar.PublicId + newCredLibs = append(newCredLibs, cl) + } + err := rw.CreateItems(context.Background(), newCredLibs) + require.NoError(err) + } + return tar +} diff --git a/internal/target/tcp/register.go b/internal/target/tcp/register.go index 68508d430..943ff8ac8 100644 --- a/internal/target/tcp/register.go +++ b/internal/target/tcp/register.go @@ -2,13 +2,15 @@ package tcp import ( "context" + "fmt" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/target" ) func init() { - target.Register(Subtype, allocTarget, vet, TargetPrefix) + target.Register(Subtype, allocTarget, vet, vetCredentialLibraries, TargetPrefix) } const ( @@ -35,3 +37,16 @@ func vet(ctx context.Context, t target.Target) error { } return nil } + +// vetCredentialLibraries checks that all of the provided credential libriaries have a CredentialPurpose +// of ApplicationPurpose. Any other CredentialPurpose will result in an error. +func vetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error { + const op = "tcp.vetCredentialLibraries" + + for _, cl := range cls { + if cl.CredentialPurpose != string(credential.ApplicationPurpose) { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("tcp.Target only supports credential purpose: %q", credential.ApplicationPurpose)) + } + } + return nil +} diff --git a/internal/target/tcp/repository_credential_source_test.go b/internal/target/tcp/repository_credential_source_test.go index ba0720773..1b0c80b28 100644 --- a/internal/target/tcp/repository_credential_source_test.go +++ b/internal/target/tcp/repository_credential_source_test.go @@ -2,10 +2,10 @@ package tcp_test import ( "context" - "sort" "testing" "time" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/store" "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -39,14 +40,14 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { type args struct { targetVersion uint32 - credLibIds []string + cls []*target.CredentialLibrary } tests := []struct { - name string - args args - wantCredLibIds []string - wantErr bool - wantErrCode errors.Code + name string + args args + wantCredSources map[string]target.CredentialSource + wantErr bool + wantErrCode errors.Code }{ { name: "zero-version", @@ -68,34 +69,95 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { name: "valid-single-source", args: args{ targetVersion: 1, - credLibIds: []string{lib1.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + }, }, - wantCredLibIds: []string{lib1.PublicId}, - wantErr: false, + wantCredSources: map[string]target.CredentialSource{ + lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: lib1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + wantErr: false, }, { name: "valid-multiple-sources", args: args{ targetVersion: 1, - credLibIds: []string{lib1.PublicId, lib2.PublicId, lib3.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib3.PublicId, credential.ApplicationPurpose), + }, }, - wantCredLibIds: []string{lib1.PublicId, lib2.PublicId, lib3.PublicId}, - wantErr: false, + wantCredSources: map[string]target.CredentialSource{ + lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: lib1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + lib2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: lib2.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + lib3.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: lib3.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + wantErr: false, }, { name: "invalid-source-id", args: args{ targetVersion: 1, - credLibIds: []string{lib1.PublicId, lib2.PublicId, lib3.PublicId, "invalid-source-id"}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib3.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", "invalid-source-id", credential.ApplicationPurpose), + }, }, wantErr: true, wantErrCode: errors.NotSpecificIntegrity, }, + { + name: "egress-credential-purpose", + args: args{ + targetVersion: 1, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "ingress-credential-purpose", + args: args{ + targetVersion: 1, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.IngressPurpose), + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, { name: "bad-version", args: args{ targetVersion: 1000, - credLibIds: []string{lib1.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + }, }, wantErr: true, wantErrCode: errors.VersionMismatch, @@ -106,20 +168,25 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { assert, require := assert.New(t), require.New(t) projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, tt.name) - gotTarget, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, tt.args.targetVersion, tt.args.credLibIds) + + for _, cl := range tt.args.cls { + cl.TargetId = projTarget.PublicId + } + + gotTarget, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, tt.args.targetVersion, tt.args.cls) if tt.wantErr { require.Error(err) assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "unexpected error %s", err.Error()) return } require.NoError(err) - assert.Len(gotCredSources, len(tt.wantCredLibIds)) - gotCredSourcesMap := map[string]target.CredentialSource{} - for _, s := range gotCredSources { - gotCredSourcesMap[s.Id()] = s - } - for _, id := range tt.wantCredLibIds { - assert.NotEmpty(gotCredSourcesMap[id]) + assert.Len(gotCredSources, len(tt.wantCredSources)) + + for _, cs := range gotCredSources { + w, ok := tt.wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] + assert.True(ok, "got unexpected credentialsource %v", cs) + assert.Equal(w.Id(), cs.Id()) + assert.Equal(w.CredentialPurpose(), cs.CredentialPurpose()) } // test to see of the target version update oplog was created @@ -132,9 +199,13 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { assert.Equal(projTarget.GetVersion(), tar.GetVersion()-1) assert.True(proto.Equal(gotTarget.(*tcp.Target), tar.(*tcp.Target))) assert.Equal(gotCredSources, lookupCredSources) - for _, s := range lookupCredSources { - assert.NotEmpty(gotCredSourcesMap[s.Id()]) - assert.Equal(projTarget.PublicId, s.TargetId()) + + for _, cs := range lookupCredSources { + w, ok := tt.wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] + assert.True(ok, "got unexpected credentialsource %v", cs) + assert.Equal(w.Id(), cs.Id()) + assert.Equal(w.CredentialPurpose(), cs.CredentialPurpose()) + assert.Equal(projTarget.PublicId, cs.TargetId()) } }) } @@ -142,18 +213,22 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { assert, require := assert.New(t), require.New(t) projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, "add-existing") - _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []string{lib1.PublicId}) + cl1 := target.TestNewCredentialLibrary(projTarget.PublicId, lib1.PublicId, credential.ApplicationPurpose) + cl2 := target.TestNewCredentialLibrary(projTarget.PublicId, lib2.PublicId, credential.ApplicationPurpose) + cl3 := target.TestNewCredentialLibrary(projTarget.PublicId, lib3.PublicId, credential.ApplicationPurpose) + + _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []*target.CredentialLibrary{cl1}) require.NoError(err) assert.Len(gotCredSources, 1) assert.Equal(lib1.PublicId, gotCredSources[0].Id()) // Adding lib1 again should error - _, _, _, err = repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []string{lib1.PublicId}) + _, _, _, err = repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []*target.CredentialLibrary{cl1}) require.Error(err) assert.True(errors.Match(errors.T(errors.NotUnique), err)) // Adding multiple with lib1 in set should error - _, _, _, err = repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []string{lib3.PublicId, lib2.PublicId, lib1.PublicId}) + _, _, _, err = repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []*target.CredentialLibrary{cl3, cl2, cl1}) require.Error(err) assert.True(errors.Match(errors.T(errors.NotUnique), err)) @@ -166,7 +241,8 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - _, _, _, err := repo.AddTargetCredentialSources(context.Background(), "fake-target-id", 1, []string{lib1.PublicId}) + cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) + _, _, _, err := repo.AddTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) @@ -275,29 +351,29 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { assert, require := assert.New(t), require.New(t) cs := css[i] - target := tcp.TestTarget(t, conn, proj.PublicId, tt.name) + tar := tcp.TestTarget(t, conn, proj.PublicId, tt.name) - clIds := make([]string, 0, tt.args.createCnt) + cls := make([]*target.CredentialLibrary, 0, tt.args.createCnt) if tt.args.createCnt > 0 { credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.PublicId, tt.args.createCnt) for _, cl := range credLibs { - clIds = append(clIds, cl.PublicId) + cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) } } - _, _, addedCredSources, err := repo.AddTargetCredentialSources(context.Background(), target.GetPublicId(), 1, clIds) + _, _, addedCredSources, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) require.NoError(err) assert.Equal(tt.args.createCnt, len(addedCredSources)) - deleteCredSources := make([]string, 0, tt.args.deleteCnt) + deleteCredSources := make([]*target.CredentialLibrary, 0, tt.args.deleteCnt) for i := 0; i < tt.args.deleteCnt; i++ { - deleteCredSources = append(deleteCredSources, clIds[i]) + deleteCredSources = append(deleteCredSources, cls[i]) } var targetId string switch { case tt.args.targetIdOverride != nil: targetId = *tt.args.targetIdOverride default: - targetId = target.GetPublicId() + targetId = tar.GetPublicId() } var targetVersion uint32 switch { @@ -317,7 +393,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { assert.Equal(tt.wantRowsDeleted, deletedRows) // we should find the oplog for the delete of target credential libraries - err = db.TestVerifyOplog(t, rw, target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) + err = db.TestVerifyOplog(t, rw, tar.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) }) } @@ -333,18 +409,22 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { lib3 := libs[2] projTarget := tcp.TestTarget(t, conn, proj.PublicId, "add-existing") - _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []string{lib1.PublicId, lib2.PublicId}) + cl1 := target.TestNewCredentialLibrary(projTarget.PublicId, lib1.PublicId, credential.ApplicationPurpose) + cl2 := target.TestNewCredentialLibrary(projTarget.PublicId, lib2.PublicId, credential.ApplicationPurpose) + cl3 := target.TestNewCredentialLibrary(projTarget.PublicId, lib3.PublicId, credential.ApplicationPurpose) + + _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []*target.CredentialLibrary{cl1, cl2}) require.NoError(err) assert.Len(gotCredSources, 2) // Deleting an unassociated source should return an error - delCount, err := repo.DeleteTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []string{lib3.PublicId}) + delCount, err := repo.DeleteTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []*target.CredentialLibrary{cl3}) require.Error(err) assert.True(errors.Match(errors.T(errors.MultipleRecords), err)) assert.Equal(0, delCount) // Deleting sources which includes an unassociated source should return an error - delCount, err = repo.DeleteTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []string{lib1.PublicId, lib2.PublicId, lib3.PublicId}) + delCount, err = repo.DeleteTargetCredentialSources(context.Background(), projTarget.PublicId, 2, []*target.CredentialLibrary{cl1, cl2, cl3}) require.Error(err) assert.True(errors.Match(errors.T(errors.MultipleRecords), err)) assert.Equal(0, delCount) @@ -373,26 +453,26 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { lib1 := credLibs[0] lib2 := credLibs[1] - setupFn := func(target target.Target) []target.CredentialSource { + setupFn := func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 10) - clIds := make([]string, 0, len(credLibs)) + cls := make([]*target.CredentialLibrary, 0, len(credLibs)) for _, cl := range credLibs { - clIds = append(clIds, cl.PublicId) + cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) } - _, _, created, err := repo.AddTargetCredentialSources(context.Background(), target.GetPublicId(), 1, clIds) + _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) require.NoError(t, err) require.Equal(t, 10, len(created)) - return created + return created, cls } type args struct { targetVersion uint32 - clIds []string + cls []*target.CredentialLibrary addToOrigLibs bool } tests := []struct { name string - setup func(target.Target) []target.CredentialSource + setup func(target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) args args wantAffectedRows int wantErr bool @@ -403,7 +483,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - clIds: []string{}, + cls: []*target.CredentialLibrary{}, }, wantErr: false, wantAffectedRows: 10, @@ -413,7 +493,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - clIds: []string{}, + cls: []*target.CredentialLibrary{}, addToOrigLibs: true, }, wantErr: false, @@ -424,7 +504,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - clIds: []string{lib1.PublicId, lib2.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, addToOrigLibs: true, }, wantErr: false, @@ -435,7 +518,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 0, - clIds: []string{lib1.PublicId, lib2.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, addToOrigLibs: true, }, wantErr: true, @@ -446,7 +532,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 1000, - clIds: []string{lib1.PublicId, lib2.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, addToOrigLibs: true, }, wantErr: true, @@ -457,12 +546,39 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - clIds: []string{lib1.PublicId, lib2.PublicId}, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + }, addToOrigLibs: false, }, wantErr: false, wantAffectedRows: 12, }, + { + name: "egress-credential-purpose", + setup: setupFn, + args: args{ + targetVersion: 1, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "ingress-credential-purpose", + setup: setupFn, + args: args{ + targetVersion: 1, + cls: []*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", lib1.PublicId, credential.IngressPurpose), + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -471,22 +587,32 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { tar := tcp.TestTarget(t, conn, proj.PublicId, tt.name) var origCredSources []target.CredentialSource + var origCredLibraries []*target.CredentialLibrary if tt.setup != nil { - origCredSources = tt.setup(tar) + origCredSources, origCredLibraries = tt.setup(tar) + } + + wantCredSources := make(map[string]target.CredentialSource) + for _, cl := range tt.args.cls { + cl.TargetId = tar.GetPublicId() + wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ + CredentialLibrary: cl.CredentialLibrary, + } } if tt.args.addToOrigLibs { - origIds := make([]string, 0, len(origCredSources)) - for _, cl := range origCredSources { - origIds = append(origIds, cl.Id()) + tt.args.cls = append(tt.args.cls, origCredLibraries...) + for _, cl := range origCredLibraries { + wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ + CredentialLibrary: cl.CredentialLibrary, + } } - tt.args.clIds = append(tt.args.clIds, origIds...) } origTarget, _, lookupCredSources, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) require.NoError(err) assert.Equal(origCredSources, lookupCredSources) - _, got, affectedRows, err := repo.SetTargetCredentialSources(context.Background(), tar.GetPublicId(), tt.args.targetVersion, tt.args.clIds) + _, got, affectedRows, err := repo.SetTargetCredentialSources(context.Background(), tar.GetPublicId(), tt.args.targetVersion, tt.args.cls) if tt.wantErr { require.Error(err) assert.Equal(0, affectedRows) @@ -496,21 +622,15 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Log(err) require.NoError(err) assert.Equal(tt.wantAffectedRows, affectedRows) - assert.Equal(len(tt.args.clIds), len(got)) - var wantIds []string - wantIds = append(wantIds, tt.args.clIds...) - sort.Strings(wantIds) + assert.Equal(len(wantCredSources), len(got)) - var gotIds []string - if len(got) > 0 { - gotIds = make([]string, 0, len(got)) - for _, s := range got { - gotIds = append(gotIds, s.Id()) - } + for _, cs := range got { + w, ok := wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] + assert.True(ok, "got unexpected credentialsource %v", cs) + assert.Equal(w.Id(), cs.Id()) + assert.Equal(w.CredentialPurpose(), cs.CredentialPurpose()) } - sort.Strings(gotIds) - assert.Equal(wantIds, gotIds) foundTarget, _, _, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) require.NoError(err) @@ -523,7 +643,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("missing-target-id", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, []string{lib1.PublicId}) + cl1 := target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, []*target.CredentialLibrary{cl1}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) @@ -531,7 +652,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, []string{lib1.PublicId}) + cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) diff --git a/internal/target/tcp/repository_tcp_target_test.go b/internal/target/tcp/repository_tcp_target_test.go index 6ae271b5c..9b2338a67 100644 --- a/internal/target/tcp/repository_tcp_target_test.go +++ b/internal/target/tcp/repository_tcp_target_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" dbassert "github.com/hashicorp/boundary/internal/db/assert" @@ -14,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/store" "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -39,9 +41,16 @@ func TestRepository_CreateTarget(t *testing.T) { cs := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] credSources := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2) - var clIds []string + var credLibraries []*target.CredentialLibrary + var credLibIds []string for _, cl := range credSources { - clIds = append(clIds, cl.PublicId) + credLibraries = append(credLibraries, &target.CredentialLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: cl.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }) + credLibIds = append(credLibIds, cl.PublicId) } type args struct { @@ -392,11 +401,22 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { cls := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 5) var testClIds []string + var testCredLibs []*target.CredentialLibrary for _, cl := range cls { + testCredLibs = append(testCredLibs, &target.CredentialLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + CredentialLibraryId: cl.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }) testClIds = append(testClIds, cl.PublicId) } - tt.newTargetOpts = append(tt.newTargetOpts, target.WithHostSources(testHostSetIds), target.WithCredentialSources(testClIds)) + tt.newTargetOpts = append( + tt.newTargetOpts, + target.WithHostSources(testHostSetIds), + target.WithCredentialLibraries(testCredLibs), + ) name := tt.newName if name == "" { name = tcp.TestId(t) diff --git a/internal/target/tcp/testing.go b/internal/target/tcp/testing.go index f78f61e9b..6e917abd4 100644 --- a/internal/target/tcp/testing.go +++ b/internal/target/tcp/testing.go @@ -36,12 +36,11 @@ func TestTarget(t *testing.T, conn *db.DB, scopeId, name string, opt ...target.O err := rw.CreateItems(context.Background(), newHostSets) require.NoError(err) } - if len(opts.WithCredentialSources) > 0 { - newCredLibs := make([]interface{}, 0, len(opts.WithCredentialSources)) - for _, cl := range opts.WithCredentialSources { - newCl, err := target.NewCredentialLibrary(tar.PublicId, cl) - require.NoError(err) - newCredLibs = append(newCredLibs, newCl) + if len(opts.WithCredentialLibraries) > 0 { + newCredLibs := make([]interface{}, 0, len(opts.WithCredentialLibraries)) + for _, cl := range opts.WithCredentialLibraries { + cl.TargetId = tar.PublicId + newCredLibs = append(newCredLibs, cl) } err := rw.CreateItems(context.Background(), newCredLibs) require.NoError(err) diff --git a/internal/target/testing.go b/internal/target/testing.go index b436a7963..9460fd382 100644 --- a/internal/target/testing.go +++ b/internal/target/testing.go @@ -4,19 +4,33 @@ import ( "context" "testing" + "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/target/store" "github.com/stretchr/testify/require" ) +// TestNewCredentialLibrary creates a new in memory CredentialLibrary +// representing the relationship between targetId and credentialLibraryId with +// the given purpose. +func TestNewCredentialLibrary(targetId, credentialLibraryId string, purpose credential.Purpose) *CredentialLibrary { + return &CredentialLibrary{ + CredentialLibrary: &store.CredentialLibrary{ + TargetId: targetId, + CredentialLibraryId: credentialLibraryId, + CredentialPurpose: string(purpose), + }, + } +} + // TestCredentialLibrary creates a CredentialLibrary for targetId and -// libraryId. +// libraryId with the credential purpose of application. func TestCredentialLibrary(t *testing.T, conn *db.DB, targetId, libraryId string) *CredentialLibrary { t.Helper() require := require.New(t) rw := db.New(conn) - lib, err := NewCredentialLibrary(targetId, libraryId) - require.NoError(err) - err = rw.Create(context.Background(), lib) + lib := TestNewCredentialLibrary(targetId, libraryId, credential.ApplicationPurpose) + err := rw.Create(context.Background(), lib) require.NoError(err) return lib } diff --git a/sdk/go.mod b/sdk/go.mod index bc7062253..76b0ca1a3 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.5 + github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-kms-wrapping v0.6.6 diff --git a/sdk/go.sum b/sdk/go.sum index 09afa3a1f..cb8ed6f7a 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -243,8 +243,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.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE= -github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= +github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/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_taggable_test.go b/sdk/pbs/controller/api/resources/authmethods/auth_method_taggable_test.go index a4018792a..aab9f930d 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 @@ -9,6 +9,7 @@ import ( "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" @@ -92,7 +93,7 @@ func TestAuthMethod_Tags(t *testing.T) { "account_claim_maps": structpb.NewStringValue("public-account_claim_maps"), "disable_discovered_config_validation": structpb.NewStringValue("public-disable_discovered_config_validation"), "dry_run": structpb.NewStringValue("public-dry_run"), - "client_secret": structpb.NewStringValue(""), + "client_secret": structpb.NewStringValue(encrypt.RedactedData), }, }, AuthorizedActions: []string{"action-1", "action-2"}, diff --git a/sdk/pbs/controller/api/resources/targets/target.pb.go b/sdk/pbs/controller/api/resources/targets/target.pb.go index cf0c3a283..388432916 100644 --- a/sdk/pbs/controller/api/resources/targets/target.pb.go +++ b/sdk/pbs/controller/api/resources/targets/target.pb.go @@ -487,6 +487,10 @@ type Target struct { ApplicationCredentialSourceIds []string `protobuf:"bytes,400,rep,name=application_credential_source_ids,proto3" json:"application_credential_source_ids,omitempty"` // Output only. The application credential sources associated with this Target. ApplicationCredentialSources []*CredentialSource `protobuf:"bytes,410,rep,name=application_credential_sources,proto3" json:"application_credential_sources,omitempty"` + // Output only. The IDs of the egress credential source ids associated with this Target. + EgressCredentialSourceIds []string `protobuf:"bytes,500,rep,name=egress_credential_source_ids,proto3" json:"egress_credential_source_ids,omitempty"` + // Output only. The egress credential sources associated with this Target. + EgressCredentialSources []*CredentialSource `protobuf:"bytes,510,rep,name=egress_credential_sources,proto3" json:"egress_credential_sources,omitempty"` // The attributes that are applicable for the specific Target. Attributes *structpb.Struct `protobuf:"bytes,200,opt,name=attributes,proto3" json:"attributes,omitempty"` // Output only. The available actions on this resource for this user. @@ -667,6 +671,20 @@ func (x *Target) GetApplicationCredentialSources() []*CredentialSource { return nil } +func (x *Target) GetEgressCredentialSourceIds() []string { + if x != nil { + return x.EgressCredentialSourceIds + } + return nil +} + +func (x *Target) GetEgressCredentialSources() []*CredentialSource { + if x != nil { + return x.EgressCredentialSources + } + return nil +} + func (x *Target) GetAttributes() *structpb.Struct { if x != nil { return x.Attributes @@ -1130,7 +1148,7 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{ 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0xe9, 0x0c, 0x0a, 0x06, 0x54, 0x61, + 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0xa4, 0x0e, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, @@ -1226,92 +1244,104 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{ 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, - 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, - 0x26, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x18, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, - 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x5a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, - 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0xf4, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x74, 0x0a, 0x19, 0x65, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0xfe, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x32, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x20, 0x0a, - 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, - 0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x0b, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x73, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3b, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x19, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x3e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0xc8, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, + 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, + 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x87, 0x01, 0x0a, 0x13, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x12, + 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x26, 0x0a, 0x0a, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x18, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, + 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x8d, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x52, + 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x96, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, + 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, + 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, + 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1363,19 +1393,20 @@ var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{ 13, // 13: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue 3, // 14: controller.api.resources.targets.v1.Target.application_credential_libraries:type_name -> controller.api.resources.targets.v1.CredentialLibrary 2, // 15: controller.api.resources.targets.v1.Target.application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource - 11, // 16: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct - 15, // 17: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value - 12, // 18: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 14, // 19: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp - 8, // 20: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo - 12, // 21: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 14, // 22: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp - 5, // 23: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential - 24, // [24:24] is the sub-list for method output_type - 24, // [24:24] is the sub-list for method input_type - 24, // [24:24] is the sub-list for extension type_name - 24, // [24:24] is the sub-list for extension extendee - 0, // [0:24] is the sub-list for field type_name + 2, // 16: controller.api.resources.targets.v1.Target.egress_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource + 11, // 17: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct + 15, // 18: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value + 12, // 19: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 14, // 20: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp + 8, // 21: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo + 12, // 22: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 14, // 23: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp + 5, // 24: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential + 25, // [25:25] is the sub-list for method output_type + 25, // [25:25] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_controller_api_resources_targets_v1_target_proto_init() } diff --git a/testing/dbtest/docker/Makefile b/testing/dbtest/docker/Makefile index 93734b9f0..0d4ff3d99 100644 --- a/testing/dbtest/docker/Makefile +++ b/testing/dbtest/docker/Makefile @@ -8,6 +8,7 @@ IMAGE_NAME=postgres IMAGE_TAG ?= $(REGISTRY_NAME)/$(IMAGE_NAME):11-alpine PG_OPTS ?= TEST_DB_PORT ?= 5432 +TEST_CONTAINER_NAME ?= boundary-sql-tests # Generate targets from dockerfiles dockerfiles = $(wildcard Dockerfile.*) @@ -29,7 +30,7 @@ database-up: @echo "Additional postgres configuration: $(PG_OPTS)" @docker run \ $(DOCKER_ARGS) \ - --name boundary-sql-tests \ + --name $(TEST_CONTAINER_NAME) \ -p $(TEST_DB_PORT):5432 \ -e POSTGRES_PASSWORD=boundary \ -e POSTGRES_USER=boundary \ @@ -40,11 +41,12 @@ database-up: $(IMAGE_TAG) \ -c 'config_file=/etc/postgresql/postgresql.conf' \ $(PG_OPTS) 1> /dev/null + @echo "Container name: $(TEST_CONTAINER_NAME)" @echo "Test database available at: 127.0.0.1:$(TEST_DB_PORT)" @echo "For database logs run:" - @echo " docker logs boundary-sql-tests" + @echo " docker logs $(TEST_CONTAINER_NAME)" clean: - docker stop boundary-sql-tests || true - docker rm -v boundary-sql-tests || true + docker stop $(TEST_CONTAINER_NAME) || true + docker rm -v $(TEST_CONTAINER_NAME) || true .PHONY: all docker-build database-up ${docker-builds} ${docker-publishes} clean diff --git a/website/package-lock.json b/website/package-lock.json index 0b24b5899..10b3f7b89 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -31,7 +31,7 @@ "@hashicorp/react-product-features-list": "^5.0.0", "@hashicorp/react-search": "^6.1.1", "@hashicorp/react-section-header": "^5.0.4", - "@hashicorp/react-subnav": "^9.0.1", + "@hashicorp/react-subnav": "^9.1.4", "@hashicorp/react-use-cases": "^5.0.0", "@hashicorp/react-vertical-text-block-list": "^7.0.0", "change-case": "4.1.2", @@ -1143,9 +1143,9 @@ } }, "node_modules/@hashicorp/react-button": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hashicorp/react-button/-/react-button-6.0.1.tgz", - "integrity": "sha512-e5Kl0Nc0V9RWxrNUrig2BQlJuwTmJeZdCZJkOcUGbCwySgQ1otWkKjLRR2nOGM3v+EAkkBxhMiyBIRS2vPNrdg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@hashicorp/react-button/-/react-button-6.0.3.tgz", + "integrity": "sha512-CRK93cWw5ap4tGGvMoyr/Ydvvz1Ie1RTyTdBhcpARlH7pCIAmvxHKMK+FCWXWGJNjpM9suKenXfdTJRfz7+KNw==", "dependencies": { "@hashicorp/platform-product-meta": "^0.1.0", "@hashicorp/react-inline-svg": "^6.0.1", @@ -1603,13 +1603,13 @@ } }, "node_modules/@hashicorp/react-subnav": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@hashicorp/react-subnav/-/react-subnav-9.0.1.tgz", - "integrity": "sha512-WRfCrkGD55qMnww+L2+tdXF04/NRY7F8XAuKqRpLpCk6ygHFvsxlsc4sn5TXpKPEPZCdcT22qlxvejjOyJ3E4g==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@hashicorp/react-subnav/-/react-subnav-9.1.4.tgz", + "integrity": "sha512-osbb+LzDwBRYp2IBHqjHpJXRIRFiDrMkRHw0k4DsAvb/3ZzUXD0EUqSXJGjjPxzC8m6/bsFXQnxcNUrFMshBZA==", "dependencies": { "@hashicorp/mktg-logos": "^1.0.2", "@hashicorp/platform-product-meta": "^0.1.0", - "@hashicorp/react-button": "^6.0.0", + "@hashicorp/react-button": "^6.0.3", "@hashicorp/react-inline-svg": "^6.0.3", "@hashicorp/react-link-wrap": "^3.0.3", "@reach/visually-hidden": "^0.16.0", @@ -21221,9 +21221,9 @@ } }, "@hashicorp/react-button": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hashicorp/react-button/-/react-button-6.0.1.tgz", - "integrity": "sha512-e5Kl0Nc0V9RWxrNUrig2BQlJuwTmJeZdCZJkOcUGbCwySgQ1otWkKjLRR2nOGM3v+EAkkBxhMiyBIRS2vPNrdg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@hashicorp/react-button/-/react-button-6.0.3.tgz", + "integrity": "sha512-CRK93cWw5ap4tGGvMoyr/Ydvvz1Ie1RTyTdBhcpARlH7pCIAmvxHKMK+FCWXWGJNjpM9suKenXfdTJRfz7+KNw==", "requires": { "@hashicorp/platform-product-meta": "^0.1.0", "@hashicorp/react-inline-svg": "^6.0.1", @@ -21597,13 +21597,13 @@ } }, "@hashicorp/react-subnav": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@hashicorp/react-subnav/-/react-subnav-9.0.1.tgz", - "integrity": "sha512-WRfCrkGD55qMnww+L2+tdXF04/NRY7F8XAuKqRpLpCk6ygHFvsxlsc4sn5TXpKPEPZCdcT22qlxvejjOyJ3E4g==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@hashicorp/react-subnav/-/react-subnav-9.1.4.tgz", + "integrity": "sha512-osbb+LzDwBRYp2IBHqjHpJXRIRFiDrMkRHw0k4DsAvb/3ZzUXD0EUqSXJGjjPxzC8m6/bsFXQnxcNUrFMshBZA==", "requires": { "@hashicorp/mktg-logos": "^1.0.2", "@hashicorp/platform-product-meta": "^0.1.0", - "@hashicorp/react-button": "^6.0.0", + "@hashicorp/react-button": "^6.0.3", "@hashicorp/react-inline-svg": "^6.0.3", "@hashicorp/react-link-wrap": "^3.0.3", "@reach/visually-hidden": "^0.16.0", diff --git a/website/package.json b/website/package.json index 4e787564e..646907dd6 100644 --- a/website/package.json +++ b/website/package.json @@ -27,7 +27,7 @@ "@hashicorp/react-product-features-list": "^5.0.0", "@hashicorp/react-search": "^6.1.1", "@hashicorp/react-section-header": "^5.0.4", - "@hashicorp/react-subnav": "^9.0.1", + "@hashicorp/react-subnav": "^9.1.4", "@hashicorp/react-use-cases": "^5.0.0", "@hashicorp/react-vertical-text-block-list": "^7.0.0", "change-case": "4.1.2",