From 8b8d2822dfbce05fab38a2ec539936df108c1ae8 Mon Sep 17 00:00:00 2001 From: Elim Tsiagbey Date: Tue, 21 Nov 2023 11:30:02 -0500 Subject: [PATCH] feat(oidc): OIDC Prompt (#4053) Boundary OIDC method does not currently support passing in prompts during authentication. This change adds the capability of passing OIDC prompts. Prompts are optional OIDC parameters that determine the behaviour of the authentication server: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest ## Changes - New `auth_oidc_prompt` table which contains all the prompts for OIDC auth method - New `auth_oidc_prompt_enm` table which contains possible enum values for a prompt - Currently supported: - `none`: The Authorization Server MUST NOT display any authentication or consent user interface pages - `login`: The Authorization Server SHOULD prompt the End-User for reauthentication - `consent`: The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client - `select_account`: The Authorization Server SHOULD prompt the End-User to select a user account - `oidc_auth_method_with_value_obj` view has been updated to return `prompt` value - Add `prompt` option for OIDC auth method CLI create and update - Pass configured prompt during OIDC authentication - Add `prompt` API validation for create and update --- .../oidc_auth_method_attributes.gen.go | 1 + api/authmethods/option.gen.go | 24 ++ internal/auth/oidc/auth_method.go | 32 +- internal/auth/oidc/auth_method_test.go | 20 + internal/auth/oidc/immutable_fields_test.go | 77 ++++ internal/auth/oidc/options.go | 8 + internal/auth/oidc/prompt.go | 117 ++++++ internal/auth/oidc/prompt_test.go | 330 +++++++++++++++ .../oidc/repository_auth_method_create.go | 9 +- .../repository_auth_method_create_test.go | 14 + .../repository_auth_method_delete_test.go | 21 + .../auth/oidc/repository_auth_method_read.go | 4 + .../oidc/repository_auth_method_read_test.go | 29 +- .../oidc/repository_auth_method_update.go | 55 ++- .../repository_auth_method_update_test.go | 72 ++++ internal/auth/oidc/service_start_auth.go | 5 + internal/auth/oidc/service_start_auth_test.go | 18 + internal/auth/oidc/store/oidc.pb.go | 387 +++++++++++------ internal/auth/oidc/testing.go | 15 +- .../cmd/commands/authmethodscmd/oidc_funcs.go | 16 + .../authmethods/authmethod_service.go | 17 + .../authmethods/authmethod_service_test.go | 71 +++- .../controller/handlers/authmethods/oidc.go | 8 + .../handlers/authmethods/oidc_test.go | 51 +++ ...add_data_key_foreign_key_references.up.sql | 2 + .../postgres/79/01_auth_oidc_prompt.up.sql | 112 +++++ internal/db/sqltest/Makefile | 3 +- .../db/sqltest/tests/auth/oidc/prompt.sql | 63 +++ .../authmethods/v1/auth_method.proto | 10 + .../storage/auth/oidc/store/v1/oidc.proto | 25 ++ .../resources/authmethods/auth_method.pb.go | 395 +++++++++--------- 31 files changed, 1667 insertions(+), 344 deletions(-) create mode 100644 internal/auth/oidc/prompt.go create mode 100644 internal/auth/oidc/prompt_test.go create mode 100644 internal/db/schema/migrations/oss/postgres/79/01_auth_oidc_prompt.up.sql create mode 100644 internal/db/sqltest/tests/auth/oidc/prompt.sql diff --git a/api/authmethods/oidc_auth_method_attributes.gen.go b/api/authmethods/oidc_auth_method_attributes.gen.go index d2bac7fe85..2409ea5f7c 100644 --- a/api/authmethods/oidc_auth_method_attributes.gen.go +++ b/api/authmethods/oidc_auth_method_attributes.gen.go @@ -26,6 +26,7 @@ type OidcAuthMethodAttributes struct { AccountClaimMaps []string `json:"account_claim_maps,omitempty"` DisableDiscoveredConfigValidation bool `json:"disable_discovered_config_validation,omitempty"` DryRun bool `json:"dry_run,omitempty"` + Prompts []string `json:"prompts,omitempty"` } func AttributesMapToOidcAuthMethodAttributes(in map[string]interface{}) (*OidcAuthMethodAttributes, error) { diff --git a/api/authmethods/option.gen.go b/api/authmethods/option.gen.go index dcba776e60..a66082e467 100644 --- a/api/authmethods/option.gen.go +++ b/api/authmethods/option.gen.go @@ -799,6 +799,30 @@ func DefaultName() Option { } } +func WithOidcAuthMethodPrompts(inPrompts []string) Option { + return func(o *options) { + raw, ok := o.postMap["attributes"] + if !ok { + raw = interface{}(map[string]interface{}{}) + } + val := raw.(map[string]interface{}) + val["prompts"] = inPrompts + o.postMap["attributes"] = val + } +} + +func DefaultOidcAuthMethodPrompts() Option { + return func(o *options) { + raw, ok := o.postMap["attributes"] + if !ok { + raw = interface{}(map[string]interface{}{}) + } + val := raw.(map[string]interface{}) + val["prompts"] = nil + o.postMap["attributes"] = val + } +} + func WithOidcAuthMethodSigningAlgorithms(inSigningAlgorithms []string) Option { return func(o *options) { raw, ok := o.postMap["attributes"] diff --git a/internal/auth/oidc/auth_method.go b/internal/auth/oidc/auth_method.go index 416132efa7..5b719eb710 100644 --- a/internal/auth/oidc/auth_method.go +++ b/internal/auth/oidc/auth_method.go @@ -107,6 +107,12 @@ func NewAuthMethod(ctx context.Context, scopeId string, clientId string, clientS a.SigningAlgs = append(a.SigningAlgs, string(alg)) } } + if len(opts.withPrompts) > 0 { + a.Prompts = make([]string, 0, len(opts.withPrompts)) + for _, prompts := range opts.withPrompts { + a.Prompts = append(a.Prompts, string(prompts)) + } + } if len(opts.withAccountClaimMap) > 0 { a.AccountClaimMaps = make([]string, 0, len(opts.withAccountClaimMap)) for k, v := range opts.withAccountClaimMap { @@ -282,6 +288,7 @@ type convertedValues struct { Certs []any ClaimsScopes []any AccountClaimMaps []any + Prompts []any } // convertValueObjects converts the embedded value objects. It will return an @@ -292,7 +299,7 @@ func (am *AuthMethod) convertValueObjects(ctx context.Context) (*convertedValues return nil, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") } var err error - var addAlgs, addAuds, addCerts, addScopes, addAccountClaimMaps []any + var addAlgs, addAuds, addCerts, addScopes, addAccountClaimMaps, addPrompts []any if addAlgs, err = am.convertSigningAlgs(ctx); err != nil { return nil, errors.Wrap(ctx, err, op) } @@ -308,12 +315,16 @@ func (am *AuthMethod) convertValueObjects(ctx context.Context) (*convertedValues if addAccountClaimMaps, err = am.convertAccountClaimMaps(ctx); err != nil { return nil, errors.Wrap(ctx, err, op) } + if addPrompts, err = am.convertPrompts(ctx); err != nil { + return nil, errors.Wrap(ctx, err, op) + } return &convertedValues{ Algs: addAlgs, Auds: addAuds, Certs: addCerts, ClaimsScopes: addScopes, AccountClaimMaps: addAccountClaimMaps, + Prompts: addPrompts, }, nil } @@ -458,3 +469,22 @@ func ParseAccountClaimMaps(ctx context.Context, m ...string) ([]ClaimMap, error) } return claimMap, nil } + +// convertPrompts converts the embedded prompts from []string +// to []interface{} where each slice element is a *Prompt. It will return an +// error if the AuthMethod's public id is not set. +func (am *AuthMethod) convertPrompts(ctx context.Context) ([]any, error) { + const op = "oidc.(AuthMethod).convertPrompts" + if am.PublicId == "" { + return nil, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + newInterfaces := make([]any, 0, len(am.Prompts)) + for _, a := range am.Prompts { + obj, err := NewPrompt(ctx, am.PublicId, PromptParam(a)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + newInterfaces = append(newInterfaces, obj) + } + return newInterfaces, nil +} diff --git a/internal/auth/oidc/auth_method_test.go b/internal/auth/oidc/auth_method_test.go index 8730954eca..11ec01b2ad 100644 --- a/internal/auth/oidc/auth_method_test.go +++ b/internal/auth/oidc/auth_method_test.go @@ -591,6 +591,14 @@ func Test_convertValueObjects(t *testing.T) { testAccountClaimMaps = append(testAccountClaimMaps, obj) } + testPrompts := []string{"consent", "select_account"} + testExpectedPrompts := make([]any, 0, len(testPrompts)) + for _, a := range testPrompts { + obj, err := NewPrompt(ctx, testPublicId, PromptParam(a)) + require.NoError(t, err) + testExpectedPrompts = append(testExpectedPrompts, obj) + } + tests := []struct { name string authMethodId string @@ -599,6 +607,7 @@ func Test_convertValueObjects(t *testing.T) { certs []string scopes []string maps []string + prompts []string wantValues *convertedValues wantErrMatch *errors.Template wantErrContains string @@ -611,12 +620,14 @@ func Test_convertValueObjects(t *testing.T) { certs: testCerts, scopes: testScopes, maps: testClaimMaps, + prompts: testPrompts, wantValues: &convertedValues{ Algs: testSigningAlgs, Auds: testAudiences, Certs: testCertificates, ClaimsScopes: testClaimsScopes, AccountClaimMaps: testAccountClaimMaps, + Prompts: testExpectedPrompts, }, }, { @@ -636,6 +647,7 @@ func Test_convertValueObjects(t *testing.T) { Certificates: tt.certs, ClaimsScopes: tt.scopes, AccountClaimMaps: tt.maps, + Prompts: tt.prompts, }, } @@ -693,6 +705,14 @@ func Test_convertValueObjects(t *testing.T) { assert.Equal(want, got) } + convertedPrompts, err := am.convertPrompts(ctx) + if tt.wantErrMatch != nil { + require.Error(err) + assert.Truef(errors.Match(tt.wantErrMatch, err), "wanted err %q and got: %+v", tt.wantErrMatch.Code, err) + } else { + assert.Equal(tt.wantValues.Prompts, convertedPrompts) + } + values, err := am.convertValueObjects(ctx) if tt.wantErrMatch != nil { require.Error(err) diff --git a/internal/auth/oidc/immutable_fields_test.go b/internal/auth/oidc/immutable_fields_test.go index 8c20980503..53225be88e 100644 --- a/internal/auth/oidc/immutable_fields_test.go +++ b/internal/auth/oidc/immutable_fields_test.go @@ -430,3 +430,80 @@ func TestAccount_ImmutableFields(t *testing.T) { }) } } + +func TestPrompt_ImmutableFields(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + rw := db.New(conn) + ctx := context.Background() + databaseWrapper, err := kmsCache.GetWrapper(ctx, org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} + + am := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name", + WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]), WithPrompts(SelectAccount)) + + new := AllocPrompt() + require.NoError(t, rw.LookupWhere(ctx, &new, "oidc_method_id = ? and prompt = ?", []any{am.PublicId, SelectAccount})) + + tests := []struct { + name string + update *Prompt + fieldMask []string + }{ + { + name: "oidc_method_id", + update: func() *Prompt { + cp := new.Clone() + cp.OidcMethodId = "p_thisIsNotAValidId" + return cp + }(), + fieldMask: []string{"PublicId"}, + }, + { + name: "create time", + update: func() *Prompt { + cp := new.Clone() + cp.CreateTime = &ts + return cp + }(), + fieldMask: []string{"CreateTime"}, + }, + { + name: "prompt", + update: func() *Prompt { + cp := new.Clone() + cp.PromptParam = string(Consent) + return cp + }(), + fieldMask: []string{"PromptParam"}, + }, + } + 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() + orig.SetTableName(defaultAuthMethodTableName) + require.NoError(rw.LookupWhere(ctx, &new, "oidc_method_id = ? and prompt = ?", []any{orig.OidcMethodId, orig.PromptParam})) + + require.NoError(err) + + tt.update.SetTableName(defaultAuthMethodTableName) + rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true)) + require.Error(err) + assert.Equal(0, rowsUpdated) + + after := new.Clone() + after.SetTableName(defaultAuthMethodTableName) + require.NoError(rw.LookupWhere(ctx, &new, "oidc_method_id = ? and prompt = ?", []any{after.OidcMethodId, after.PromptParam})) + + assert.True(proto.Equal(orig, after)) + }) + } +} diff --git a/internal/auth/oidc/options.go b/internal/auth/oidc/options.go index b7614e9785..3ab4341aa8 100644 --- a/internal/auth/oidc/options.go +++ b/internal/auth/oidc/options.go @@ -33,6 +33,7 @@ type options struct { withAudClaims []string withSigningAlgs []Alg withClaimsScopes []string + withPrompts []PromptParam withEmail string withFullName string withOrderByCreateTime bool @@ -232,3 +233,10 @@ func WithReader(reader db.Reader) Option { o.withReader = reader } } + +// WithPrompts provides optional prompts +func WithPrompts(prompt ...PromptParam) Option { + return func(o *options) { + o.withPrompts = prompt + } +} diff --git a/internal/auth/oidc/prompt.go b/internal/auth/oidc/prompt.go new file mode 100644 index 0000000000..0b3028691f --- /dev/null +++ b/internal/auth/oidc/prompt.go @@ -0,0 +1,117 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package oidc + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/auth/oidc/store" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/cap/oidc" + "google.golang.org/protobuf/proto" +) + +// Prompt represents OIDC authentication prompt +type PromptParam string + +const ( + // Prompt values defined by OpenID specs. + // See: https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters + None PromptParam = "none" + Login PromptParam = "login" + Consent PromptParam = "consent" + SelectAccount PromptParam = "select_account" +) + +var supportedPrompts = map[PromptParam]bool{ + None: true, + Login: true, + Consent: true, + SelectAccount: true, +} + +// SupportedPrompt returns true if the provided prompt is supported +// by boundary. +func SupportedPrompt(p PromptParam) bool { + return supportedPrompts[p] +} + +// defaultPromptTableName defines the default table name for a Prompt +const defaultPromptTableName = "auth_oidc_prompt" + +// Prompt defines an prompt supported by an OIDC auth method. +// It is assigned to an OIDC AuthMethod and updates/deletes to that AuthMethod +// are cascaded to its Prompts. Prompts are value objects of an AuthMethod, +// therefore there's no need for oplog metadata, since only the AuthMethod will have +// metadata because it's the root aggregate. +type Prompt struct { + *store.Prompt + tableName string +} + +// NewPrompt creates a new in memory prompt assigned to an OIDC +// AuthMethod. It supports no options. +func NewPrompt(ctx context.Context, authMethodId string, p PromptParam) (*Prompt, error) { + const op = "oidc.NewPrompt" + prompt := &Prompt{ + Prompt: &store.Prompt{ + OidcMethodId: authMethodId, + PromptParam: string(p), + }, + } + if err := prompt.validate(ctx, op); err != nil { + return nil, err // intentionally not wrapped + } + return prompt, nil +} + +// validate the Prompt. On success, it will return nil. +func (s *Prompt) validate(ctx context.Context, caller errors.Op) error { + if s.OidcMethodId == "" { + return errors.New(ctx, errors.InvalidParameter, caller, "missing oidc auth method id") + } + if _, ok := supportedPrompts[PromptParam(s.PromptParam)]; !ok { + return errors.New(ctx, errors.InvalidParameter, caller, fmt.Sprintf("unsupported prompt: %s", s.Prompt)) + } + return nil +} + +func convertToOIDCPrompts(ctx context.Context, p []string) []oidc.Prompt { + prompts := make([]oidc.Prompt, 0, len(p)) + for _, a := range p { + prompt := oidc.Prompt(a) + prompts = append(prompts, prompt) + } + + return prompts +} + +// AllocPrompt makes an empty one in memory +func AllocPrompt() Prompt { + return Prompt{ + Prompt: &store.Prompt{}, + } +} + +// Clone a Prompt +func (s *Prompt) Clone() *Prompt { + cp := proto.Clone(s.Prompt) + return &Prompt{ + Prompt: cp.(*store.Prompt), + } +} + +// TableName returns the table name. +func (s *Prompt) TableName() string { + if s.tableName != "" { + return s.tableName + } + return defaultPromptTableName +} + +// SetTableName sets the table name. +func (s *Prompt) SetTableName(n string) { + s.tableName = n +} diff --git a/internal/auth/oidc/prompt_test.go b/internal/auth/oidc/prompt_test.go new file mode 100644 index 0000000000..de83188012 --- /dev/null +++ b/internal/auth/oidc/prompt_test.go @@ -0,0 +1,330 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package oidc + +import ( + "context" + "testing" + + "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/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestPrompts_Create(t *testing.T) { + t.Parallel() + ctx := context.TODO() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + rw := db.New(conn) + + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + testAuthMethod := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0])) + + type args struct { + authMethodId string + prompt PromptParam + } + tests := []struct { + name string + args args + want *Prompt + wantErr bool + wantIsErr errors.Code + create bool + wantCreateErr bool + wantCreateIsErr errors.Code + }{ + { + name: "valid", + args: args{ + authMethodId: testAuthMethod.PublicId, + prompt: SelectAccount, + }, + create: true, + want: func() *Prompt { + want := AllocPrompt() + want.OidcMethodId = testAuthMethod.PublicId + want.PromptParam = string(SelectAccount) + return &want + }(), + }, + { + name: "dup", // must follow "valid" test. Prompt must be be unique for an OidcMethodId + args: args{ + authMethodId: testAuthMethod.PublicId, + prompt: SelectAccount, + }, + create: true, + want: func() *Prompt { + want := AllocPrompt() + want.OidcMethodId = testAuthMethod.PublicId + want.PromptParam = string(SelectAccount) + return &want + }(), + wantCreateErr: true, + wantCreateIsErr: errors.NotUnique, + }, + { + name: "empty-auth-method", + args: args{ + authMethodId: "", + prompt: Consent, + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + { + name: "empty-prompt", + args: args{ + authMethodId: testAuthMethod.PublicId, + prompt: "", + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + { + name: "supported-prompt", + args: args{ + authMethodId: testAuthMethod.PublicId, + prompt: PromptParam("EVE256"), // The unsupported evesdropper 256 curve + }, + wantErr: true, + wantIsErr: errors.InvalidParameter, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewPrompt(ctx, tt.args.authMethodId, tt.args.prompt) + if tt.wantErr { + require.Error(err) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) + return + } + require.NoError(err) + assert.Equal(tt.want, got) + if tt.create { + ctx := context.Background() + err = rw.Create(ctx, got) + if tt.wantCreateErr { + assert.Error(err) + assert.True(errors.Match(errors.T(tt.wantCreateIsErr), err)) + return + } else { + assert.NoError(err) + } + found := AllocPrompt() + require.NoError(rw.LookupWhere(ctx, &found, "oidc_method_id = ? and prompt = ?", []any{tt.args.authMethodId, string(tt.args.prompt)})) + assert.Equal(got, &found) + } + }) + } +} + +func TestPrompt_Delete(t *testing.T) { + t.Parallel() + ctx := context.TODO() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + rw := db.New(conn) + + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + testAuthMethod := TestAuthMethod( + t, + conn, + databaseWrapper, + org.PublicId, + InactiveState, + "alice_rp", + "my-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), + WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]), + WithPrompts(Consent)) + + testResource := func(authMethodId string, prompt PromptParam) *Prompt { + c, err := NewPrompt(ctx, authMethodId, prompt) + require.NoError(t, err) + return c + } + tests := []struct { + name string + Prompt *Prompt + wantRowsDeleted int + overrides func(*Prompt) + wantErr bool + wantErrMsg string + }{ + { + name: "valid", + Prompt: testResource(testAuthMethod.PublicId, SelectAccount), + wantErr: false, + wantRowsDeleted: 1, + }, + { + name: "bad-OidcMethodId", + Prompt: testResource(testAuthMethod.PublicId, Login), + overrides: func(c *Prompt) { c.OidcMethodId = "bad-id" }, + wantErr: false, + wantRowsDeleted: 0, + }, + { + name: "bad-prompt", + Prompt: testResource(testAuthMethod.PublicId, None), + overrides: func(c *Prompt) { c.PromptParam = "bad-prompt" }, + wantErr: false, + wantRowsDeleted: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + cp := tt.Prompt.Clone() + require.NoError(rw.Create(ctx, &cp)) + + if tt.overrides != nil { + tt.overrides(cp) + } + deletedRows, err := rw.Delete(ctx, &cp) + if tt.wantErr { + require.Error(err) + return + } + require.NoError(err) + if tt.wantRowsDeleted == 0 { + assert.Equal(tt.wantRowsDeleted, deletedRows) + return + } + assert.Equal(tt.wantRowsDeleted, deletedRows) + found := AllocPrompt() + err = rw.LookupWhere(ctx, &found, "oidc_method_id = ? and prompt = ?", []any{tt.Prompt.OidcMethodId, tt.Prompt.String()}) + assert.Truef(errors.IsNotFoundError(err), "unexpected error: %s", err.Error()) + }) + } +} + +func TestPrompt_Clone(t *testing.T) { + t.Parallel() + ctx := context.TODO() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + t.Run("valid", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(err) + m := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0])) + orig, err := NewPrompt(ctx, m.PublicId, Consent) + require.NoError(err) + cp := orig.Clone() + assert.True(proto.Equal(cp.Prompt, orig.Prompt)) + }) + t.Run("not-equal", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(err) + m := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0])) + orig, err := NewPrompt(ctx, m.PublicId, Consent) + require.NoError(err) + orig2, err := NewPrompt(ctx, m.PublicId, SelectAccount) + require.NoError(err) + + cp := orig.Clone() + assert.True(!proto.Equal(cp.Prompt, orig2.Prompt)) + }) +} + +func TestPrompt_SetTableName(t *testing.T) { + t.Parallel() + defaultTableName := defaultPromptTableName + tests := []struct { + name string + setNameTo string + want string + }{ + { + name: "new-name", + setNameTo: "new-name", + want: "new-name", + }, + { + name: "reset to default", + setNameTo: "", + want: defaultTableName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + def := AllocPrompt() + require.Equal(defaultTableName, def.TableName()) + m := AllocPrompt() + m.SetTableName(tt.setNameTo) + assert.Equal(tt.want, m.TableName()) + }) + } +} + +func TestPrompt_SupportedPrompt(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + prompt PromptParam + want bool + }{ + { + name: "none-prompt", + prompt: None, + want: true, + }, + { + name: "login-prompt", + prompt: Login, + want: true, + }, + { + name: "consent-prompt", + prompt: Consent, + want: true, + }, + { + name: "select-account-prompt", + prompt: SelectAccount, + want: true, + }, + { + name: "invalid-prompt", + prompt: "invalid", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + got := SupportedPrompt(tt.prompt) + assert.Equal(tt.want, got) + }) + } +} diff --git a/internal/auth/oidc/repository_auth_method_create.go b/internal/auth/oidc/repository_auth_method_create.go index 1a0db12a68..a4f8383811 100644 --- a/internal/auth/oidc/repository_auth_method_create.go +++ b/internal/auth/oidc/repository_auth_method_create.go @@ -16,7 +16,7 @@ import ( // CreateAuthMethod creates am (*AuthMethod) in the repo along with its // associated embedded optional value objects of SigningAlgs, AudClaims, -// and Certificates and returns the newly created AuthMethod +// Prompts, and Certificates and returns the newly created AuthMethod // (with its PublicId set) // // The AuthMethod's public id and version must be empty (zero values). @@ -123,6 +123,13 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, am *AuthMethod, opt . } msgs = append(msgs, accountClaimMapsOplogMsgs...) } + if len(vo.Prompts) > 0 { + promptOplogMsgs := make([]*oplog.Message, 0, len(vo.Prompts)) + if err := w.CreateItems(ctx, vo.Prompts, db.NewOplogMsgs(&promptOplogMsgs)); err != nil { + return err + } + msgs = append(msgs, promptOplogMsgs...) + } metadata := am.oplog(oplog.OpType_OP_TYPE_CREATE) if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) diff --git a/internal/auth/oidc/repository_auth_method_create_test.go b/internal/auth/oidc/repository_auth_method_create_test.go index 1959d99a07..253100f4c7 100644 --- a/internal/auth/oidc/repository_auth_method_create_test.go +++ b/internal/auth/oidc/repository_auth_method_create_test.go @@ -36,6 +36,14 @@ func TestRepository_CreateAuthMethod(t *testing.T) { } return s } + + convertPrompts := func(prompts ...PromptParam) []string { + s := make([]string, 0, len(prompts)) + for _, a := range prompts { + s = append(s, string(a)) + } + return s + } tests := []struct { name string am func(*testing.T) *AuthMethod @@ -48,6 +56,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { algs := []Alg{RS256, ES256} cbs := TestConvertToUrls(t, "https://www.alice.com/callback")[0] auds := []string{"alice-rp", "bob-rp"} + prompts := []PromptParam{"consent", "select_account"} cert1, pem1 := testGenerateCA(t, "localhost") cert2, pem2 := testGenerateCA(t, "localhost") certs := []*x509.Certificate{cert1, cert2} @@ -65,6 +74,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { WithName("alice's restaurant"), WithDescription("it's a good place to eat"), WithClaimsScopes("email", "profile"), + WithPrompts(prompts...), WithAccountClaimMap(map[string]AccountToClaim{"display_name": ToNameClaim, "oid": ToSubClaim}), ) require.NoError(t, err) @@ -74,6 +84,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { require.Equal(t, am.AudClaims, auds) require.Equal(t, am.Certificates, pems) require.Equal(t, am.OperationalState, string(InactiveState)) + require.Equal(t, am.Prompts, convertPrompts(prompts...)) return am }, }, @@ -83,6 +94,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { algs := []Alg{RS256, ES256} cbs := TestConvertToUrls(t, "https://www.alice.com/callback")[0] auds := []string{"alice-rp-custom", "bob-rp-custom"} + prompts := []PromptParam{"consent", "select_account"} cert1, pem1 := testGenerateCA(t, "localhost") cert2, pem2 := testGenerateCA(t, "localhost") certs := []*x509.Certificate{cert1, cert2} @@ -97,6 +109,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { WithApiUrl(cbs), WithSigningAlgs(algs...), WithCertificates(certs...), + WithPrompts(prompts...), WithName("alice's restaurant with a twist"), WithDescription("it's an okay but kinda weird place to eat"), WithClaimsScopes("email", "profile"), @@ -109,6 +122,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) { require.Equal(t, am.AudClaims, auds) require.Equal(t, am.Certificates, pems) require.Equal(t, am.OperationalState, string(InactiveState)) + require.Equal(t, am.Prompts, convertPrompts(prompts...)) return am }, opt: []Option{WithPublicId("amoidc_1234567890")}, diff --git a/internal/auth/oidc/repository_auth_method_delete_test.go b/internal/auth/oidc/repository_auth_method_delete_test.go index da841f69e9..fce74a86bc 100644 --- a/internal/auth/oidc/repository_auth_method_delete_test.go +++ b/internal/auth/oidc/repository_auth_method_delete_test.go @@ -41,6 +41,27 @@ func TestRepository_DeleteAuthMethod(t *testing.T) { }(), wantRowsDeleted: 1, }, + { + name: "valid-with-prompts", + authMethod: func() *AuthMethod { + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + return TestAuthMethod( + t, + conn, + databaseWrapper, + org.PublicId, + InactiveState, + "alice_rp", + "alices-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), + WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]), + WithPrompts(SelectAccount), + ) + }(), + wantRowsDeleted: 1, + }, { name: "no-public-id", authMethod: func() *AuthMethod { am := AllocAuthMethod(); return &am }(), diff --git a/internal/auth/oidc/repository_auth_method_read.go b/internal/auth/oidc/repository_auth_method_read.go index 8bff4a957d..277b8b5c73 100644 --- a/internal/auth/oidc/repository_auth_method_read.go +++ b/internal/auth/oidc/repository_auth_method_read.go @@ -167,6 +167,9 @@ func (r *Repository) getAuthMethods(ctx context.Context, authMethodId string, sc if agg.AccountClaimMaps != "" { am.AccountClaimMaps = strings.Split(agg.AccountClaimMaps, aggregateDelimiter) } + if agg.Prompts != "" { + am.Prompts = strings.Split(agg.Prompts, aggregateDelimiter) + } authMethods = append(authMethods, &am) } return authMethods, nil @@ -198,6 +201,7 @@ type authMethodAgg struct { Certs string ClaimsScopes string AccountClaimMaps string + Prompts string } // TableName returns the table name for gorm diff --git a/internal/auth/oidc/repository_auth_method_read_test.go b/internal/auth/oidc/repository_auth_method_read_test.go index 77b7de58e5..3ee2fde90d 100644 --- a/internal/auth/oidc/repository_auth_method_read_test.go +++ b/internal/auth/oidc/repository_auth_method_read_test.go @@ -33,7 +33,8 @@ func TestRepository_LookupAuthMethod(t *testing.T) { "alice_rp", "alices-dogs-name", WithAccountClaimMap(map[string]AccountToClaim{"oid": ToSubClaim, "display_name": ToNameClaim}), WithApiUrl(TestConvertToUrls(t, "https://alice-active-priv.com/callback")[0]), - WithSigningAlgs(RS256)) + WithSigningAlgs(RS256), + WithPrompts(Consent, SelectAccount)) amActivePub := TestAuthMethod( t, conn, databaseWrapper, org.PublicId, ActivePublicState, @@ -152,6 +153,32 @@ func TestRepository_ListAuthMethods(t *testing.T) { }, opt: []Option{WithLimit(1), WithOrderByCreateTime(true)}, }, + { + name: "with-prompts", + setupFn: func() ([]string, []*AuthMethod, string) { + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + am1a := TestAuthMethod( + t, + conn, + databaseWrapper, + org.PublicId, + InactiveState, + "alice_rp", + "alices-dogs-name", + WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), + WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]), + WithClaimsScopes("email", "profile"), + WithPrompts(Consent, SelectAccount), + ) + iam.TestSetPrimaryAuthMethod(t, iamRepo, org, am1a.PublicId) + am1a.IsPrimaryAuthMethod = true + + return []string{am1a.ScopeId}, []*AuthMethod{am1a}, am1a.PublicId + }, + }, { name: "no-search-criteria", setupFn: func() ([]string, []*AuthMethod, string) { diff --git a/internal/auth/oidc/repository_auth_method_update.go b/internal/auth/oidc/repository_auth_method_update.go index c24d184dc7..5a1029373e 100644 --- a/internal/auth/oidc/repository_auth_method_update.go +++ b/internal/auth/oidc/repository_auth_method_update.go @@ -40,6 +40,7 @@ const ( TokenClaimsField = "TokenClaims" UserinfoClaimsField = "UserinfoClaims" KeyIdField = "KeyId" + PromptsField = "Prompts" ) // UpdateAuthMethod will retrieve the auth method from the repository, @@ -59,7 +60,7 @@ const ( // be updated. Fields will be set to NULL if the field is a // zero value and included in fieldMask. Name, Description, Issuer, // ClientId, ClientSecret, MaxAge are all updatable fields. The AuthMethod's -// Value Objects of SigningAlgs, CallbackUrls, AudClaims and Certificates are +// Value Objects of SigningAlgs, Prompts, CallbackUrls, AudClaims and Certificates are // also updatable. if no updatable fields are included in the fieldMaskPaths, // then an error is returned. // @@ -109,6 +110,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi CertificatesField: am.Certificates, ClaimsScopesField: am.ClaimsScopes, AccountClaimMapsField: am.AccountClaimMaps, + PromptsField: am.Prompts, }, fieldMaskPaths, nil, @@ -177,6 +179,12 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi if err != nil { return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) } + + addPrompts, deletePrompts, err := valueObjectChanges(ctx, origAm.PublicId, PromptsVO, am.Prompts, origAm.Prompts, dbMask, nullFields) + if err != nil { + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + // we don't allow updates for "sub" claim maps, because we have no way to // determine if the updated "from" claim in the map might create collisions // with any existing account's subject. @@ -196,7 +204,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi var filteredDbMask, filteredNullFields []string for _, f := range dbMask { switch f { - case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField: + case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField, PromptsField: continue default: filteredDbMask = append(filteredDbMask, f) @@ -204,7 +212,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi } for _, f := range nullFields { switch f { - case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField: + case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField, PromptsField: continue default: filteredNullFields = append(filteredNullFields, f) @@ -223,7 +231,9 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi len(addScopes) == 0 && len(deleteScopes) == 0 && len(addMaps) == 0 && - len(deleteMaps) == 0 { + len(deleteMaps) == 0 && + len(addPrompts) == 0 && + len(deletePrompts) == 0 { return origAm, db.NoRowsAffected, nil } @@ -259,7 +269,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { - msgs := make([]*oplog.Message, 0, 7) // AuthMethod, Algs*2, Certs*2, Audiences*2 + msgs := make([]*oplog.Message, 0, 9) // AuthMethod, Algs*2, Certs*2, Audiences*2, Prompts*2 ticket, err := w.GetTicket(ctx, am) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) @@ -310,6 +320,25 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi msgs = append(msgs, addAlgsOplogMsgs...) } + if len(deletePrompts) > 0 { + deletePromptOplogMsgs := make([]*oplog.Message, 0, len(deletePrompts)) + rowsDeleted, err := w.DeleteItems(ctx, deletePrompts, db.NewOplogMsgs(&deletePromptOplogMsgs)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete prompts")) + } + if rowsDeleted != len(deletePrompts) { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("prompts deleted %d did not match request for %d", rowsDeleted, len(deletePrompts))) + } + msgs = append(msgs, deletePromptOplogMsgs...) + } + if len(addPrompts) > 0 { + addPromptsOplogMsgs := make([]*oplog.Message, 0, len(addPrompts)) + if err := w.CreateItems(ctx, addPrompts, db.NewOplogMsgs(&addPromptsOplogMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add prompts")) + } + msgs = append(msgs, addPromptsOplogMsgs...) + } + if len(deleteCerts) > 0 { deleteCertOplogMsgs := make([]*oplog.Message, 0, len(deleteCerts)) rowsDeleted, err := w.DeleteItems(ctx, deleteCerts, db.NewOplogMsgs(&deleteCertOplogMsgs)) @@ -426,12 +455,13 @@ const ( AudClaimVO voName = "AudClaims" ClaimsScopesVO voName = "ClaimsScopes" AccountClaimMapsVO voName = "AccountClaimMaps" + PromptsVO voName = "Prompts" ) // validVoName decides if the name is valid func validVoName(name voName) bool { switch name { - case SigningAlgVO, CertificateVO, AudClaimVO, ClaimsScopesVO, AccountClaimMapsVO: + case SigningAlgVO, CertificateVO, AudClaimVO, ClaimsScopesVO, AccountClaimMapsVO, PromptsVO: return true default: return false @@ -478,6 +508,10 @@ var supportedFactories = map[voName]factoryFunc{ } return NewAccountClaimMap(ctx, publicId, m.From, to) }, + PromptsVO: func(ctx context.Context, publicId string, i any) (any, error) { + str := fmt.Sprintf("%s", i) + return NewPrompt(ctx, publicId, PromptParam(str)) + }, } // valueObjectChanges takes the new and old list of VOs (value objects) and @@ -580,6 +614,7 @@ func validateFieldMask(ctx context.Context, fieldMaskPaths []string) error { case strings.EqualFold(CertificatesField, f): case strings.EqualFold(ClaimsScopesField, f): case strings.EqualFold(AccountClaimMapsField, f): + case strings.EqualFold(PromptsField, f): default: return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("invalid field mask: %s", f)) } @@ -646,6 +681,14 @@ func applyUpdate(new, orig *AuthMethod, fieldMaskPaths []string) *AuthMethod { cp.AccountClaimMaps = make([]string, 0, len(new.AccountClaimMaps)) cp.AccountClaimMaps = append(cp.AccountClaimMaps, new.AccountClaimMaps...) } + case PromptsField: + switch { + case len(new.Prompts) == 0: + cp.Prompts = nil + default: + cp.Prompts = make([]string, 0, len(new.Prompts)) + cp.Prompts = append(cp.Prompts, new.Prompts...) + } } } return cp diff --git a/internal/auth/oidc/repository_auth_method_update_test.go b/internal/auth/oidc/repository_auth_method_update_test.go index 31027d344a..48b77b572d 100644 --- a/internal/auth/oidc/repository_auth_method_update_test.go +++ b/internal/auth/oidc/repository_auth_method_update_test.go @@ -529,6 +529,71 @@ func Test_UpdateAuthMethod(t *testing.T) { version: 2, // since TestAuthMethod(...) did an update to get it to ActivePublicState wantErrMatch: errors.T(errors.InvalidParameter), }, + { + name: "update-with-prompt", + setup: func() *AuthMethod { + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + return TestAuthMethod( + t, + conn, + databaseWrapper, + org.PublicId, + InactiveState, + "alice-rp", + "alice-secret", + WithCertificates(tpCert[0]), + WithSigningAlgs(Alg(tpAlg)), + ) + }, + updateWith: func(orig *AuthMethod) *AuthMethod { + am := AllocAuthMethod() + am.PublicId = orig.PublicId + am.Prompts = []string{string(SelectAccount)} + return &am + }, + fieldMasks: []string{PromptsField}, + version: 1, + want: func(orig, updateWith *AuthMethod) *AuthMethod { + am := orig.Clone() + am.Prompts = updateWith.Prompts + return am + }, + }, + { + name: "update-with-existing-prompt", + setup: func() *AuthMethod { + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + return TestAuthMethod( + t, + conn, + databaseWrapper, + org.PublicId, + InactiveState, + "alice-rp", + "alice-secret", + WithCertificates(tpCert[0]), + WithSigningAlgs(Alg(tpAlg)), + WithPrompts(Consent), + ) + }, + updateWith: func(orig *AuthMethod) *AuthMethod { + am := AllocAuthMethod() + am.PublicId = orig.PublicId + am.Prompts = []string{string(SelectAccount)} + return &am + }, + fieldMasks: []string{PromptsField}, + version: 1, + want: func(orig, updateWith *AuthMethod) *AuthMethod { + am := orig.Clone() + am.Prompts = updateWith.Prompts + return am + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1068,6 +1133,7 @@ func Test_validateFieldMask(t *testing.T) { AudClaimsField, CertificatesField, ClaimsScopesField, + PromptsField, }, }, { @@ -1114,6 +1180,7 @@ func Test_applyUpdate(t *testing.T) { AudClaims: []string{"new-aud-1", "new-aud-2"}, Certificates: []string{"new-pem1", "new-pem-2"}, ClaimsScopes: []string{"new-scope1", "new-scope2"}, + Prompts: []string{string(SelectAccount)}, }, }, orig: &AuthMethod{ @@ -1130,6 +1197,7 @@ func Test_applyUpdate(t *testing.T) { AudClaims: []string{"orig-aud-1", "orig-aud-2"}, Certificates: []string{"orig-pem1", "orig-pem-2"}, ClaimsScopes: []string{"orig-scope1", "orig-scope2"}, + Prompts: []string{string(None)}, }, }, want: &AuthMethod{ @@ -1146,6 +1214,7 @@ func Test_applyUpdate(t *testing.T) { AudClaims: []string{"new-aud-1", "new-aud-2"}, Certificates: []string{"new-pem1", "new-pem-2"}, ClaimsScopes: []string{"new-scope1", "new-scope2"}, + Prompts: []string{string(SelectAccount)}, }, }, fieldMask: []string{ @@ -1160,6 +1229,7 @@ func Test_applyUpdate(t *testing.T) { AudClaimsField, CertificatesField, ClaimsScopesField, + PromptsField, }, }, { @@ -1189,6 +1259,7 @@ func Test_applyUpdate(t *testing.T) { AudClaims: []string{"orig-aud-1", "orig-aud-2"}, Certificates: []string{"orig-pem1", "orig-pem-2"}, ClaimsScopes: []string{"orig-scope1", "orig-scope2"}, + Prompts: []string{string(SelectAccount)}, }, }, want: &AuthMethod{ @@ -1214,6 +1285,7 @@ func Test_applyUpdate(t *testing.T) { AudClaimsField, CertificatesField, ClaimsScopesField, + PromptsField, }, }, } diff --git a/internal/auth/oidc/service_start_auth.go b/internal/auth/oidc/service_start_auth.go index 30078b8230..78adf46381 100644 --- a/internal/auth/oidc/service_start_auth.go +++ b/internal/auth/oidc/service_start_auth.go @@ -118,6 +118,11 @@ func StartAuth(ctx context.Context, oidcRepoFn OidcRepoFactory, authMethodId str oidcOpts = append(oidcOpts, oidc.WithScopes(am.ClaimsScopes...)) } + if len(am.Prompts) > 0 { + prompts := convertToOIDCPrompts(ctx, am.Prompts) + oidcOpts = append(oidcOpts, oidc.WithPrompts(prompts...)) + } + // a bare min oidc.Request needed for the provider.AuthURL(...) call. We've intentionally not populated // things like Audiences, because this oidc.Request isn't cached and not intended for use in future legs // of the authen flow. diff --git a/internal/auth/oidc/service_start_auth_test.go b/internal/auth/oidc/service_start_auth_test.go index 1dfe8c714c..9864dfa1ce 100644 --- a/internal/auth/oidc/service_start_auth_test.go +++ b/internal/auth/oidc/service_start_auth_test.go @@ -33,6 +33,7 @@ func Test_StartAuth(t *testing.T) { _, _, tpAlg, _ := tp.SigningKeys() tpCert, err := ParseCertificates(ctx, tp.CACert()) require.NoError(t, err) + tpPrompt := []PromptParam{Consent, SelectAccount} conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) rootWrapper := db.TestWrapper(t) @@ -77,6 +78,16 @@ func Test_StartAuth(t *testing.T) { WithMaxAge(-1), ) + testAuthMethodWithPrompt := TestAuthMethod( + t, conn, databaseWrapper, org.PublicId, ActivePublicState, + "test-rp4", "fido", + WithIssuer(TestConvertToUrls(t, tp.Addr())[0]), + WithApiUrl(TestConvertToUrls(t, testController.URL)[0]), + WithSigningAlgs(Alg(tpAlg)), + WithCertificates(tpCert...), + WithPrompts(tpPrompt...), + ) + stdSetup := func(am *AuthMethod, repoFn OidcRepoFactory, apiSrv *httptest.Server) (a *AuthMethod, allowedRedirect string) { // update the allowed redirects for the TestProvider tpAllowedRedirect := fmt.Sprintf(CallbackEndpoint, apiSrv.URL) @@ -154,6 +165,13 @@ func Test_StartAuth(t *testing.T) { wantErrMatch: errors.T(errors.RecordNotFound), wantErrContains: "auth method not-valid not found:", }, + { + name: "simple-with-prompt", + repoFn: repoFn, + apiSrv: testController, + authMethod: testAuthMethodWithPrompt, + setup: stdSetup, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/auth/oidc/store/oidc.pb.go b/internal/auth/oidc/store/oidc.pb.go index aea9302212..485701d608 100644 --- a/internal/auth/oidc/store/oidc.pb.go +++ b/internal/auth/oidc/store/oidc.pb.go @@ -125,6 +125,13 @@ type AuthMethod struct { // to_claim. For example "oid=sub". // @inject_tag: `gorm:"-"` AccountClaimMaps []string `protobuf:"bytes,210,rep,name=account_claim_maps,json=accountClaimMaps,proto3" json:"account_claim_maps,omitempty" gorm:"-"` + // prompts are the optional prompts allowed for an oidc auth method. + // These value objects specify whether the authorization server prompts + // the end-user for reauthentication, account selection and consent. + // These are Value Objects that will be stored as Prompt messages, + // and are operatated on as a complete set. + // @inject_tag: `gorm:"-"` + Prompts []string `protobuf:"bytes,220,rep,name=prompts,proto3" json:"prompts,omitempty" gorm:"-"` } func (x *AuthMethod) Reset() { @@ -320,6 +327,13 @@ func (x *AuthMethod) GetAccountClaimMaps() []string { return nil } +func (x *AuthMethod) GetPrompts() []string { + if x != nil { + return x.Prompts + } + return nil +} + // Account represents an OIDC account // the scope_id column is not included here as it is used only to ensure // data integrity in the database between iam users and auth methods. @@ -1041,6 +1055,75 @@ func (x *ManagedGroupMemberAccount) GetMemberId() string { return "" } +// Prompt entries are the prompts allowed for an oidc auth method. +type Prompt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: `gorm:"primary_key"` + OidcMethodId string `protobuf:"bytes,10,opt,name=oidc_method_id,json=oidcMethodId,proto3" json:"oidc_method_id,omitempty" gorm:"primary_key"` + // prompt_param is an enum from the auth_oidc_prompt_enm table + // @inject_tag: `gorm:"primary_key;column:prompt"` + PromptParam string `protobuf:"bytes,20,opt,name=prompt_param,json=promptParam,proto3" json:"prompt_param,omitempty" gorm:"primary_key;column:prompt"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` +} + +func (x *Prompt) Reset() { + *x = Prompt{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Prompt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Prompt) ProtoMessage() {} + +func (x *Prompt) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[9] + 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 Prompt.ProtoReflect.Descriptor instead. +func (*Prompt) Descriptor() ([]byte, []int) { + return file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescGZIP(), []int{9} +} + +func (x *Prompt) GetOidcMethodId() string { + if x != nil { + return x.OidcMethodId + } + return "" +} + +func (x *Prompt) GetPromptParam() string { + if x != nil { + return x.PromptParam + } + return "" +} + +func (x *Prompt) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + var File_controller_storage_auth_oidc_store_v1_oidc_proto protoreflect.FileDescriptor var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{ @@ -1055,7 +1138,7 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x74, 0x6f, 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, 0x22, 0x83, 0x0b, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x0b, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 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, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -1143,133 +1226,147 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{ 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x52, 0x10, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x9a, 0x04, 0x0a, - 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 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, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 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, 0x1e, 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, - 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, - 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 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, 0x32, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, - 0x0a, 0x0b, 0x44, 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, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, - 0x72, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x75, 0x6c, - 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, - 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, - 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x21, 0x0a, 0x0c, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x78, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, - 0x28, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x63, 0x6c, 0x61, 0x69, - 0x6d, 0x73, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x69, - 0x6e, 0x66, 0x6f, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0a, 0x53, 0x69, - 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, - 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x6c, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6c, 0x67, - 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x3c, 0x0a, 0x07, + 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x18, 0xdc, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x21, + 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x12, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x22, 0x9a, 0x04, 0x0a, 0x07, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 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, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x14, 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, 0x1e, 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, 0x22, 0x8f, 0x01, - 0x0a, 0x08, 0x41, 0x75, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, - 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x75, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x75, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x1e, 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, 0x22, - 0x94, 0x01, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, - 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 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, 0x22, 0x96, 0x01, 0x0a, 0x0b, 0x43, 0x6c, 0x61, 0x69, 0x6d, - 0x73, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, + 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, + 0x0c, 0x0a, 0x04, 0x4e, 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, 0x32, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, + 0x44, 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, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, + 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, + 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x6e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x28, 0x0a, + 0x0f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, + 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, + 0x6f, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x1e, 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, 0x22, - 0xbe, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, - 0x4d, 0x61, 0x70, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, - 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x6f, - 0x6d, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, - 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x5f, 0x63, - 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x6f, 0x43, 0x6c, - 0x61, 0x69, 0x6d, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x28, 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, - 0x22, 0xa6, 0x03, 0x0a, 0x0c, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, - 0x70, 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, 0x4b, - 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, + 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x6c, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6c, 0x67, 0x12, 0x4b, + 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 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, 0x1e, 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, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 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, 0x32, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 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, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, - 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, - 0x12, 0x37, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x1f, 0xc2, 0xdd, 0x29, 0x1b, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x11, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xaf, 0x01, 0x0a, 0x19, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x08, + 0x41, 0x75, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, + 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x75, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x75, 0x64, + 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x1e, 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, 0x22, 0x94, 0x01, + 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, + 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 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, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x1b, - 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x42, 0x3e, 0x5a, 0x3c, 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, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x54, 0x69, 0x6d, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0b, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x53, + 0x63, 0x6f, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, + 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x1e, 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, 0x22, 0xbe, 0x01, + 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, + 0x70, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x5f, + 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x72, 0x6f, + 0x6d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x5f, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x6f, 0x43, 0x6c, 0x61, 0x69, + 0x6d, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x28, 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, 0x22, 0xa6, + 0x03, 0x0a, 0x0c, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 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, 0x4b, 0x0a, 0x0b, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 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, 0x1e, 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, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 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, 0x32, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 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, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, + 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x37, + 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1f, + 0xc2, 0xdd, 0x29, 0x1b, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x11, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xaf, 0x01, 0x0a, 0x19, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 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, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x22, 0x9e, 0x01, 0x0a, 0x06, 0x50, 0x72, + 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, + 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6d, 0x70, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x4b, 0x0a, + 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 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, 0x42, 0x3e, 0x5a, 0x3c, 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, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0x2f, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1284,7 +1381,7 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescGZIP() []byte return file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescData } -var file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_controller_storage_auth_oidc_store_v1_oidc_proto_goTypes = []interface{}{ (*AuthMethod)(nil), // 0: controller.storage.auth.oidc.store.v1.AuthMethod (*Account)(nil), // 1: controller.storage.auth.oidc.store.v1.Account @@ -1295,26 +1392,28 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_goTypes = []interface{ (*AccountClaimMap)(nil), // 6: controller.storage.auth.oidc.store.v1.AccountClaimMap (*ManagedGroup)(nil), // 7: controller.storage.auth.oidc.store.v1.ManagedGroup (*ManagedGroupMemberAccount)(nil), // 8: controller.storage.auth.oidc.store.v1.ManagedGroupMemberAccount - (*timestamp.Timestamp)(nil), // 9: controller.storage.timestamp.v1.Timestamp + (*Prompt)(nil), // 9: controller.storage.auth.oidc.store.v1.Prompt + (*timestamp.Timestamp)(nil), // 10: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_auth_oidc_store_v1_oidc_proto_depIdxs = []int32{ - 9, // 0: controller.storage.auth.oidc.store.v1.AuthMethod.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 1: controller.storage.auth.oidc.store.v1.AuthMethod.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 2: controller.storage.auth.oidc.store.v1.Account.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 3: controller.storage.auth.oidc.store.v1.Account.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 4: controller.storage.auth.oidc.store.v1.SigningAlg.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 5: controller.storage.auth.oidc.store.v1.AudClaim.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 6: controller.storage.auth.oidc.store.v1.Certificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 7: controller.storage.auth.oidc.store.v1.ClaimsScope.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 8: controller.storage.auth.oidc.store.v1.AccountClaimMap.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 9: controller.storage.auth.oidc.store.v1.ManagedGroup.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 10: controller.storage.auth.oidc.store.v1.ManagedGroup.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 9, // 11: controller.storage.auth.oidc.store.v1.ManagedGroupMemberAccount.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 10, // 0: controller.storage.auth.oidc.store.v1.AuthMethod.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 1: controller.storage.auth.oidc.store.v1.AuthMethod.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 2: controller.storage.auth.oidc.store.v1.Account.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 3: controller.storage.auth.oidc.store.v1.Account.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 4: controller.storage.auth.oidc.store.v1.SigningAlg.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 5: controller.storage.auth.oidc.store.v1.AudClaim.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 6: controller.storage.auth.oidc.store.v1.Certificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 7: controller.storage.auth.oidc.store.v1.ClaimsScope.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 8: controller.storage.auth.oidc.store.v1.AccountClaimMap.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 9: controller.storage.auth.oidc.store.v1.ManagedGroup.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 10: controller.storage.auth.oidc.store.v1.ManagedGroup.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 11: controller.storage.auth.oidc.store.v1.ManagedGroupMemberAccount.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 10, // 12: controller.storage.auth.oidc.store.v1.Prompt.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_controller_storage_auth_oidc_store_v1_oidc_proto_init() } @@ -1431,6 +1530,18 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_init() { return nil } } + file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Prompt); 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{ @@ -1438,7 +1549,7 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/auth/oidc/testing.go b/internal/auth/oidc/testing.go index 5056bc7b86..fa4e2d33a5 100644 --- a/internal/auth/oidc/testing.go +++ b/internal/auth/oidc/testing.go @@ -39,8 +39,8 @@ import ( const TestFakeManagedGroupFilter = `"/foo" == "bar"` // TestAuthMethod creates a test oidc auth method. WithName, WithDescription, -// WithMaxAge, WithApiUrl, WithIssuer, WithCertificates, WithAudClaims, and -// WithSigningAlgs options are supported. +// WithMaxAge, WithApiUrl, WithIssuer, WithCertificates, WithAudClaims, +// WithSigningAlgs and WithPrompts options are supported. func TestAuthMethod( t testing.TB, conn *db.DB, @@ -123,6 +123,17 @@ func TestAuthMethod( require.NoError(rw.CreateItems(ctx, newAccountClaimMaps)) require.Equal(len(opts.withAccountClaimMap), len(authMethod.AccountClaimMaps)) } + if len(opts.withPrompts) > 0 { + newPrompts := make([]any, 0, len(opts.withPrompts)) + for _, p := range opts.withPrompts { + prompt, err := NewPrompt(ctx, authMethod.PublicId, p) + require.NoError(err) + newPrompts = append(newPrompts, prompt) + } + err := rw.CreateItems(ctx, newPrompts) + require.NoError(err) + require.Equal(len(opts.withPrompts), len(authMethod.Prompts)) + } authMethod.OperationalState = string(state) rowsUpdated, err := rw.Update(ctx, authMethod, []string{OperationalStateField}, nil) require.NoError(err) diff --git a/internal/cmd/commands/authmethodscmd/oidc_funcs.go b/internal/cmd/commands/authmethodscmd/oidc_funcs.go index 5b66e8a79c..be6e288be3 100644 --- a/internal/cmd/commands/authmethodscmd/oidc_funcs.go +++ b/internal/cmd/commands/authmethodscmd/oidc_funcs.go @@ -33,6 +33,7 @@ type extraOidcCmdVars struct { flagAccountClaimMaps []string flagDisableDiscoveredConfigValidation bool flagDryRun bool + flagPrompts []string } const ( @@ -50,6 +51,7 @@ const ( stateFlagName = "state" disableDiscoveredConfigValidationFlagName = "disable-discovered-config-validation" dryRunFlagName = "dry-run" + promptsFlagName = "prompts" ) func extraOidcActionsFlagsMapFuncImpl() map[string][]string { @@ -65,6 +67,7 @@ func extraOidcActionsFlagsMapFuncImpl() map[string][]string { allowedAudienceFlagName, claimsScopes, accountClaimMaps, + promptsFlagName, }, "change-state": { idFlagName, @@ -159,6 +162,12 @@ func extraOidcFlagsFuncImpl(c *OidcCommand, set *base.FlagSets, _ *base.FlagSet) Target: &c.flagDryRun, Usage: "Performs all completeness and validation checks with any newly-provided values without persisting the changes.", }) + case promptsFlagName: + f.StringSliceVar(&base.StringSliceVar{ + Name: promptsFlagName, + Target: &c.flagPrompts, + Usage: "The optional prompt parameter that can be included in the authentication request to control the behavior of the authentication flow.", + }) } } } @@ -283,6 +292,13 @@ func extraOidcFlagHandlingFuncImpl(c *OidcCommand, f *base.FlagSets, opts *[]aut if c.flagDryRun { *opts = append(*opts, authmethods.WithOidcAuthMethodDryRun(c.flagDryRun)) } + switch { + case len(c.flagPrompts) == 0: + case len(c.flagPrompts) == 1 && c.flagPrompts[0] == "null": + *opts = append(*opts, authmethods.DefaultOidcAuthMethodPrompts()) + default: + *opts = append(*opts, authmethods.WithOidcAuthMethodPrompts(c.flagPrompts)) + } return true } diff --git a/internal/daemon/controller/handlers/authmethods/authmethod_service.go b/internal/daemon/controller/handlers/authmethods/authmethod_service.go index 834ca8769a..4d5968c865 100644 --- a/internal/daemon/controller/handlers/authmethods/authmethod_service.go +++ b/internal/daemon/controller/handlers/authmethods/authmethod_service.go @@ -862,6 +862,7 @@ func toAuthMethodProto(ctx context.Context, in auth.AuthMethod, opt ...handlers. AllowedAudiences: i.GetAudClaims(), ClaimsScopes: i.GetClaimsScopes(), AccountClaimMaps: i.GetAccountClaimMaps(), + Prompts: i.GetPrompts(), } if i.DisableDiscoveredConfigValidation { attrs.DisableDiscoveredConfigValidation = true @@ -1031,6 +1032,14 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateAuthMethodRequest } } } + if len(attrs.GetPrompts()) > 0 { + for _, p := range attrs.GetPrompts() { + if !oidc.SupportedPrompt(oidc.PromptParam(p)) { + badFields[promptsField] = fmt.Sprintf("Contains unsupported prompt %q", p) + break + } + } + } if strings.TrimSpace(attrs.GetApiUrlPrefix().GetValue()) == "" { // TODO: When we start accepting the address used in the request make this an optional field. badFields[apiUrlPrefixField] = "This field is required." @@ -1159,6 +1168,14 @@ func validateUpdateRequest(ctx context.Context, req *pbs.UpdateAuthMethodRequest } } } + if len(attrs.GetPrompts()) > 0 { + for _, p := range attrs.GetPrompts() { + if !oidc.SupportedPrompt(oidc.PromptParam(p)) { + badFields[promptsField] = fmt.Sprintf("Contains unsupported prompt %q", p) + break + } + } + } if len(attrs.GetIdpCaCerts()) > 0 { if _, err := oidc.ParseCertificates(ctx, attrs.GetIdpCaCerts()...); err != nil { badFields[idpCaCertsField] = fmt.Sprintf("Cannot parse CA certificates. %v", err.Error()) diff --git a/internal/daemon/controller/handlers/authmethods/authmethod_service_test.go b/internal/daemon/controller/handlers/authmethods/authmethod_service_test.go index 1e75b98283..dfce01e409 100644 --- a/internal/daemon/controller/handlers/authmethods/authmethod_service_test.go +++ b/internal/daemon/controller/handlers/authmethods/authmethod_service_test.go @@ -143,7 +143,8 @@ func TestGet(t *testing.T) { databaseWrapper, err := kmsCache.GetWrapper(context.Background(), o.GetPublicId(), kms.KeyPurposeDatabase) require.NoError(t, err) oidcam := oidc.TestAuthMethod(t, conn, databaseWrapper, o.GetPublicId(), oidc.InactiveState, "alice_rp", "secret", - oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0])) + oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0]), + oidc.WithPrompts(oidc.SelectAccount)) wantOidc := &pb.AuthMethod{ Id: oidcam.GetPublicId(), @@ -159,6 +160,7 @@ func TestGet(t *testing.T) { State: string(oidc.InactiveState), ApiUrlPrefix: wrapperspb.String("https://api.com"), CallbackUrl: fmt.Sprintf(oidc.CallbackEndpoint, "https://api.com"), + Prompts: []string{string(oidc.SelectAccount)}, }, }, Version: 1, @@ -303,7 +305,8 @@ func TestList(t *testing.T) { databaseWrapper, err := kmsCache.GetWrapper(context.Background(), oWithAuthMethods.GetPublicId(), kms.KeyPurposeDatabase) require.NoError(t, err) oidcam := oidc.TestAuthMethod(t, conn, databaseWrapper, oWithAuthMethods.GetPublicId(), oidc.ActivePublicState, "alice_rp", "secret", - oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0]), oidc.WithSigningAlgs(oidc.EdDSA)) + oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0]), oidc.WithSigningAlgs(oidc.EdDSA), + oidc.WithPrompts(oidc.Consent)) iam.TestSetPrimaryAuthMethod(t, iamRepo, oWithAuthMethods, oidcam.GetPublicId()) wantSomeAuthMethods = append(wantSomeAuthMethods, &pb.AuthMethod{ @@ -325,6 +328,7 @@ func TestList(t *testing.T) { SigningAlgorithms: []string{ string(oidc.EdDSA), }, + Prompts: []string{string(oidc.Consent)}, }, }, IsPrimary: true, @@ -533,7 +537,8 @@ func TestDelete(t *testing.T) { databaseWrapper, err := kmsCache.GetWrapper(context.Background(), o.GetPublicId(), kms.KeyPurposeDatabase) require.NoError(t, err) oidcam := oidc.TestAuthMethod(t, conn, databaseWrapper, o.GetPublicId(), oidc.InactiveState, "alice_rp", "my-dogs-name", - oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0])) + oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://api.com")[0]), + oidc.WithPrompts(oidc.SelectAccount)) ldapAm := ldap.TestAuthMethod(t, conn, databaseWrapper, o.GetPublicId(), []string{"ldaps://ldap1"}) @@ -1368,6 +1373,66 @@ func TestCreate(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), errContains: "invalid attributes.account_attribute_maps (unable to parse)", }, + { + name: "OIDC AuthMethod With Unsupported Prompt", + req: &pbs.CreateAuthMethodRequest{Item: &pb.AuthMethod{ + ScopeId: o.GetPublicId(), + Type: oidc.Subtype.String(), + Attrs: &pb.AuthMethod_OidcAuthMethodsAttributes{ + OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ + ApiUrlPrefix: wrapperspb.String("https://api.com"), + Issuer: wrapperspb.String("https://example2.discovery.url:4821"), + ClientId: wrapperspb.String("someclientid"), + ClientSecret: wrapperspb.String("secret"), + Prompts: []string{string(oidc.SelectAccount), "invalid"}, + }, + }, + }}, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + errContains: "Contains unsupported prompt", + }, + { + name: "Create OIDC AuthMethod With Supported Prompt", + req: &pbs.CreateAuthMethodRequest{Item: &pb.AuthMethod{ + ScopeId: o.GetPublicId(), + Type: oidc.Subtype.String(), + Attrs: &pb.AuthMethod_OidcAuthMethodsAttributes{ + OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ + Issuer: wrapperspb.String("https://example.discovery.url:4821/.well-known/openid-configuration/"), + ClientId: wrapperspb.String("exampleclientid"), + ClientSecret: wrapperspb.String("secret"), + ApiUrlPrefix: wrapperspb.String("https://callback.prefix:9281/path"), + Prompts: []string{string(oidc.SelectAccount)}, + }, + }, + }}, + idPrefix: globals.OidcAuthMethodPrefix + "_", + res: &pbs.CreateAuthMethodResponse{ + Uri: fmt.Sprintf("auth-methods/%s_", globals.OidcAuthMethodPrefix), + Item: &pb.AuthMethod{ + Id: defaultAm.GetPublicId(), + ScopeId: o.GetPublicId(), + CreatedTime: defaultAm.GetCreateTime().GetTimestamp(), + UpdatedTime: defaultAm.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: o.GetPublicId(), Type: o.GetType(), ParentScopeId: scope.Global.String()}, + Version: 1, + Type: oidc.Subtype.String(), + Attrs: &pb.AuthMethod_OidcAuthMethodsAttributes{ + OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ + Issuer: wrapperspb.String("https://example.discovery.url:4821/"), + ClientId: wrapperspb.String("exampleclientid"), + ClientSecretHmac: "", + State: string(oidc.InactiveState), + ApiUrlPrefix: wrapperspb.String("https://callback.prefix:9281/path"), + CallbackUrl: "https://callback.prefix:9281/path/v1/auth-methods/oidc:authenticate:callback", + Prompts: []string{string(oidc.SelectAccount)}, + }, + }, + AuthorizedActions: oidcAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + }, + }, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/daemon/controller/handlers/authmethods/oidc.go b/internal/daemon/controller/handlers/authmethods/oidc.go index 1cef993e48..df7d4abaa3 100644 --- a/internal/daemon/controller/handlers/authmethods/oidc.go +++ b/internal/daemon/controller/handlers/authmethods/oidc.go @@ -46,6 +46,7 @@ const ( codeField = "attributes.code" claimsScopesField = "attributes.claims_scopes" accountClaimMapsField = "attributes.account_claim_maps" + promptsField = "attributes.prompts" ) var oidcMaskManager handlers.MaskManager @@ -445,6 +446,13 @@ func toStorageOidcAuthMethod(ctx context.Context, scopeId string, in *pb.AuthMet if len(signAlgs) > 0 { opts = append(opts, oidc.WithSigningAlgs(signAlgs...)) } + var prompts []oidc.PromptParam + for _, a := range attrs.GetPrompts() { + prompts = append(prompts, oidc.PromptParam(a)) + } + if len(prompts) > 0 { + opts = append(opts, oidc.WithPrompts(prompts...)) + } if len(attrs.GetAllowedAudiences()) > 0 { opts = append(opts, oidc.WithAudClaims(attrs.GetAllowedAudiences()...)) } diff --git a/internal/daemon/controller/handlers/authmethods/oidc_test.go b/internal/daemon/controller/handlers/authmethods/oidc_test.go index 4eb04cad25..a60275cf5b 100644 --- a/internal/daemon/controller/handlers/authmethods/oidc_test.go +++ b/internal/daemon/controller/handlers/authmethods/oidc_test.go @@ -128,6 +128,7 @@ func getSetup(t *testing.T) setup { oidc.WithApiUrl(oidc.TestConvertToUrls(t, ret.testController.URL)[0]), oidc.WithSigningAlgs(oidc.Alg(ret.testProviderAlg)), oidc.WithCertificates(ret.testProviderCaCert...), + oidc.WithPrompts(oidc.Consent), ) ret.testProviderAllowedRedirect = fmt.Sprintf(oidc.CallbackEndpoint, ret.testController.URL) @@ -281,6 +282,7 @@ func TestUpdate_OIDC(t *testing.T) { tpClientSecret := "her-dog's-name" tp.SetClientCreds(tpClientId, tpClientSecret) _, _, tpAlg, _ := tp.SigningKeys() + tpPrompt := capoidc.None defaultAttributes := &pb.AuthMethod_OidcAuthMethodsAttributes{ OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ @@ -290,6 +292,7 @@ func TestUpdate_OIDC(t *testing.T) { ApiUrlPrefix: wrapperspb.String("https://example.com"), IdpCaCerts: []string{tp.CACert()}, SigningAlgorithms: []string{string(tpAlg)}, + Prompts: []string{string(tpPrompt)}, }, } defaultReadAttributes := &pb.AuthMethod_OidcAuthMethodsAttributes{ @@ -302,6 +305,7 @@ func TestUpdate_OIDC(t *testing.T) { CallbackUrl: "https://example.com/v1/auth-methods/oidc:authenticate:callback", IdpCaCerts: []string{tp.CACert()}, SigningAlgorithms: []string{string(tpAlg)}, + Prompts: []string{string(tpPrompt)}, }, } @@ -995,6 +999,53 @@ func TestUpdate_OIDC(t *testing.T) { }, }, }, + { + name: "Unsupported Prompts", + req: &pbs.UpdateAuthMethodRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"attributes.prompts"}, + }, + Item: &pb.AuthMethod{ + Attrs: &pb.AuthMethod_OidcAuthMethodsAttributes{ + OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ + Prompts: []string{string("invalid")}, + }, + }, + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Update Prompt With Valid Values", + req: &pbs.UpdateAuthMethodRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"attributes.prompts"}, + }, + Item: &pb.AuthMethod{ + Attrs: &pb.AuthMethod_OidcAuthMethodsAttributes{ + OidcAuthMethodsAttributes: &pb.OidcAuthMethodAttributes{ + Prompts: []string{string(oidc.Consent), string(oidc.SelectAccount)}, + }, + }, + }, + }, + res: &pbs.UpdateAuthMethodResponse{ + Item: &pb.AuthMethod{ + ScopeId: o.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "default"}, + Description: &wrapperspb.StringValue{Value: "default"}, + Type: oidc.Subtype.String(), + Attrs: func() *pb.AuthMethod_OidcAuthMethodsAttributes { + f := proto.Clone(defaultReadAttributes.OidcAuthMethodsAttributes).(*pb.OidcAuthMethodAttributes) + f.Prompts = []string{string(oidc.Consent), string(oidc.SelectAccount)} + return &pb.AuthMethod_OidcAuthMethodsAttributes{OidcAuthMethodsAttributes: f} + }(), + Scope: defaultScopeInfo, + AuthorizedActions: oidcAuthorizedActions, + AuthorizedCollectionActions: authorizedCollectionActions, + }, + }, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/db/schema/migrations/oss/postgres/56/02_add_data_key_foreign_key_references.up.sql b/internal/db/schema/migrations/oss/postgres/56/02_add_data_key_foreign_key_references.up.sql index 3e85d65563..e3e8f28777 100644 --- a/internal/db/schema/migrations/oss/postgres/56/02_add_data_key_foreign_key_references.up.sql +++ b/internal/db/schema/migrations/oss/postgres/56/02_add_data_key_foreign_key_references.up.sql @@ -101,6 +101,8 @@ begin; alter table credential_static_ssh_private_key_credential alter column key_id type kms_private_id; + +-- Recreated in 79/01_auth_oidc_prompt.up.sql create view oidc_auth_method_with_value_obj as select case when s.primary_auth_method_id is not null then diff --git a/internal/db/schema/migrations/oss/postgres/79/01_auth_oidc_prompt.up.sql b/internal/db/schema/migrations/oss/postgres/79/01_auth_oidc_prompt.up.sql new file mode 100644 index 0000000000..5f45ba8266 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/79/01_auth_oidc_prompt.up.sql @@ -0,0 +1,112 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: BUSL-1.1 + +begin; + +-- auth_oidc_prompt_enm entries define the supported oidc auth method prompts. +create table auth_oidc_prompt_enm ( + name text primary key + constraint only_predefined_auth_oidc_prompts_allowed + check ( + name in ( + 'none', + 'login', + 'consent', + 'select_account' + ) + ) +); + + -- define the immutable fields for auth_oidc_prompt_enm (all of them) +create trigger immutable_columns before update on auth_oidc_prompt_enm + for each row execute procedure immutable_columns('name'); + +insert into auth_oidc_prompt_enm (name) values + ('none'), + ('login'), + ('consent'), + ('select_account'); + +-- auth_oidc_prompt entries are the prompts allowed for an oidc auth method. +create table auth_oidc_prompt ( + create_time wt_timestamp, + oidc_method_id wt_public_id + constraint auth_oidc_method_fkey + references auth_oidc_method(public_id) + on delete cascade + on update cascade, + prompt text + constraint auth_oidc_prompt_enm_fkey + references auth_oidc_prompt_enm(name) + on delete restrict + on update cascade, + primary key(oidc_method_id, prompt) +); +comment on table auth_oidc_prompt is + 'auth_oidc_prompt entries are the prompts allowed for an oidc auth method.'; + +create trigger immutable_columns before update on auth_oidc_prompt + for each row execute procedure immutable_columns('create_time', 'oidc_method_id', 'prompt'); + +create trigger + default_create_time_column +before +insert on auth_oidc_prompt + for each row execute procedure default_create_time(); + +-- we will drop the oidc_auth_method_with_value_obj view, so we can recreate it +-- and add the oidc prompts to the returned set. +drop view oidc_auth_method_with_value_obj; + +-- oidc_auth_method_with_value_obj is useful for reading an oidc auth method +-- with its associated value objects (algs, auds, certs, claims scopes, +-- account claim maps and prompts) as columns with | delimited values. The +-- use of the postgres string_agg(...) to aggregate the value objects into a +-- column works because we are only pulling in one column from the associated +-- tables and that value is part of the primary key and unique. This view +-- will make things like recursive listing of oidc auth methods fairly +-- straightforward to implement for the oidc repo. The view also includes an +-- is_primary_auth_method bool +create view oidc_auth_method_with_value_obj as +select + case when s.primary_auth_method_id is not null then + true + else false end + as is_primary_auth_method, + am.public_id, + am.scope_id, + am.name, + am.description, + am.create_time, + am.update_time, + am.version, + am.state, + am.api_url, + am.disable_discovered_config_validation, + am.issuer, + am.client_id, + am.client_secret, + am.client_secret_hmac, + am.key_id, + am.max_age, + -- the string_agg(..) column will be null if there are no associated value objects + string_agg(distinct alg.signing_alg_name, '|') as algs, + string_agg(distinct aud.aud_claim, '|') as auds, + string_agg(distinct cert.certificate, '|') as certs, + string_agg(distinct cs.scope, '|') as claims_scopes, + string_agg(distinct p.prompt, '|') as prompts, + string_agg(distinct concat_ws('=', acm.from_claim, acm.to_claim), '|') as account_claim_maps +from + auth_oidc_method am + left outer join iam_scope s on am.public_id = s.primary_auth_method_id + left outer join auth_oidc_signing_alg alg on am.public_id = alg.oidc_method_id + left outer join auth_oidc_aud_claim aud on am.public_id = aud.oidc_method_id + left outer join auth_oidc_certificate cert on am.public_id = cert.oidc_method_id + left outer join auth_oidc_scope cs on am.public_id = cs.oidc_method_id + left outer join auth_oidc_account_claim_map acm on am.public_id = acm.oidc_method_id + left outer join auth_oidc_prompt p on am.public_id = p.oidc_method_id +group by am.public_id, is_primary_auth_method; -- there can be only one public_id + is_primary_auth_method, so group by isn't a problem. +comment on view oidc_auth_method_with_value_obj is +'oidc auth method with its associated value objects (algs, auds, certs, scopes, prompts) as columns with | delimited values'; + +commit; \ No newline at end of file diff --git a/internal/db/sqltest/Makefile b/internal/db/sqltest/Makefile index 8ef54093d4..3dafeebca3 100644 --- a/internal/db/sqltest/Makefile +++ b/internal/db/sqltest/Makefile @@ -32,7 +32,8 @@ TESTS ?= tests/setup/*.sql \ tests/storage/*.sql \ tests/domain/*.sql \ tests/history/*.sql \ - tests/recording/*.sql + tests/recording/*.sql \ + tests/auth/*/*.sql POSTGRES_DOCKER_IMAGE_BASE ?= postgres diff --git a/internal/db/sqltest/tests/auth/oidc/prompt.sql b/internal/db/sqltest/tests/auth/oidc/prompt.sql new file mode 100644 index 0000000000..253f801af7 --- /dev/null +++ b/internal/db/sqltest/tests/auth/oidc/prompt.sql @@ -0,0 +1,63 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: BUSL-1.1 + +begin; + select plan(5); + select wtt_load('widgets', 'iam', 'kms', 'auth'); + + -- Try to insert invalid auth_oidc_prompt value to test constraint + prepare invalid_auth_oidc_prompt_value as + insert into auth_oidc_prompt + (oidc_method_id, prompt) + values + ('aom___widget', 'invalid'); + + select throws_ok( + 'invalid_auth_oidc_prompt_value', + '23503', + 'insert or update on table "auth_oidc_prompt" violates foreign key constraint "auth_oidc_prompt_enm_fkey"', + 'inserting a row with invalid auth_oidc_prompt value' + ); + + -- Insert valid valid_auth_oidc_prompt_value value to test constraint with a valid value + prepare valid_auth_oidc_prompt_value as + insert into auth_oidc_prompt + (oidc_method_id, prompt) + values + ('aom___widget', 'select_account'); + select lives_ok('valid_auth_oidc_prompt_value'); + + -- Update immutable prompt field + prepare update_auth_oidc_prompt_value as + update auth_oidc_prompt + set prompt = 'consent' + where oidc_method_id = 'aom___widget'; + + select throws_ok( + 'update_auth_oidc_prompt_value', + '23601', + 'immutable column: auth_oidc_prompt.prompt', + 'updating an immutable auth_oidc_prompt column' + ); + + -- validate oidc_auth_method_with_value_obj view + select has_view('oidc_auth_method_with_value_obj', 'view for reading an oidc auth method with its associated value objects does not exist'); + + insert into auth_oidc_prompt + (oidc_method_id, prompt) + values + ('aom___widget', 'consent'); + + prepare select_oidc_auth_method_aggregate as + select public_id::text, prompts::text + from oidc_auth_method_with_value_obj + where public_id = 'aom___widget'; + + select results_eq( + 'select_oidc_auth_method_aggregate', + $$VALUES + ('aom___widget', 'consent|select_account')$$ + ); + + select * from finish(); +rollback; \ No newline at end of file diff --git a/internal/proto/controller/api/resources/authmethods/v1/auth_method.proto b/internal/proto/controller/api/resources/authmethods/v1/auth_method.proto index cc22f2b7ce..ad4fe60bb1 100644 --- a/internal/proto/controller/api/resources/authmethods/v1/auth_method.proto +++ b/internal/proto/controller/api/resources/authmethods/v1/auth_method.proto @@ -264,6 +264,16 @@ message OidcAuthMethodAttributes { json_name = "dry_run", (custom_options.v1.generate_sdk_option) = true ]; // @gotags: `class:"public"` + + // The prompts allowed for the auth method. + repeated string prompts = 140 [ + json_name = "prompts", + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "attributes.prompts" + that: "Prompts" + } + ]; // @gotags: `class:"public"` } // The structure of the OIDC authenticate start response, in the JSON object diff --git a/internal/proto/controller/storage/auth/oidc/store/v1/oidc.proto b/internal/proto/controller/storage/auth/oidc/store/v1/oidc.proto index 3922897db1..f6558cc034 100644 --- a/internal/proto/controller/storage/auth/oidc/store/v1/oidc.proto +++ b/internal/proto/controller/storage/auth/oidc/store/v1/oidc.proto @@ -163,6 +163,17 @@ message AuthMethod { this: "AccountClaimMaps" that: "attributes.account_claim_maps" }]; + + // prompts are the optional prompts allowed for an oidc auth method. + // These value objects specify whether the authorization server prompts + // the end-user for reauthentication, account selection and consent. + // These are Value Objects that will be stored as Prompt messages, + // and are operatated on as a complete set. + // @inject_tag: `gorm:"-"` + repeated string prompts = 220 [(custom_options.v1.mask_mapping) = { + this: "Prompts" + that: "attributes.prompts" + }]; } // Account represents an OIDC account @@ -364,3 +375,17 @@ message ManagedGroupMemberAccount { // @inject_tag: `gorm:"primary_key"` string member_id = 30; } + +// Prompt entries are the prompts allowed for an oidc auth method. +message Prompt { + // @inject_tag: `gorm:"primary_key"` + string oidc_method_id = 10; + + // prompt_param is an enum from the auth_oidc_prompt_enm table + // @inject_tag: `gorm:"primary_key;column:prompt"` + string prompt_param = 20; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 30; +} diff --git a/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go b/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go index fe8df0445f..eb76ea77af 100644 --- a/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go +++ b/sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go @@ -372,6 +372,8 @@ type OidcAuthMethodAttributes struct { // along with the updated fields applied to the resource (but not persisted) as // a result of the update request. DryRun bool `protobuf:"varint,130,opt,name=dry_run,proto3" json:"dry_run,omitempty" class:"public"` // @gotags: `class:"public"` + // The prompts allowed for the auth method. + Prompts []string `protobuf:"bytes,140,rep,name=prompts,proto3" json:"prompts,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *OidcAuthMethodAttributes) Reset() { @@ -511,6 +513,13 @@ func (x *OidcAuthMethodAttributes) GetDryRun() bool { return false } +func (x *OidcAuthMethodAttributes) GetPrompts() []string { + if x != nil { + return x.Prompts + } + return nil +} + // The structure of the OIDC authenticate start response, in the JSON object type OidcAuthMethodAuthenticateStartResponse struct { state protoimpl.MessageState @@ -1244,7 +1253,7 @@ var file_controller_api_resources_authmethods_v1_auth_method_proto_rawDesc = []b 0x6e, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x11, 0x4d, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xe6, 0x09, 0x0a, 0x18, 0x4f, 0x69, + 0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xa8, 0x0a, 0x0a, 0x18, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x59, 0x0a, 0x06, @@ -1323,201 +1332,205 @@ var file_controller_api_resources_authmethods_v1_auth_method_proto_rawDesc = []b 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, - 0x75, 0x6e, 0x22, 0x61, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x29, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, - 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x32, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x22, - 0x5c, 0x0a, 0x2a, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x22, 0x44, 0x0a, - 0x26, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x5f, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb9, 0x13, 0x0a, 0x18, 0x4c, 0x64, 0x61, 0x70, 0x41, - 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x10, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x6c, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x42, 0x28, 0xa0, 0xda, 0x29, 0x01, + 0x75, 0x6e, 0x12, 0x40, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x8c, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x12, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x70, + 0x74, 0x73, 0x12, 0x07, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x73, 0x22, 0x61, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, + 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x29, 0x4f, 0x69, 0x64, 0x63, + 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x69, + 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, + 0x69, 0x22, 0x5c, 0x0a, 0x2a, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2e, 0x0a, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x22, + 0x44, 0x0a, 0x26, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, + 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb9, 0x13, 0x0a, 0x18, 0x4c, 0x64, 0x61, + 0x70, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x10, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x6c, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x42, 0x28, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x20, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6c, 0x73, 0x12, 0x08, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x54, 0x6c, 0x73, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6c, + 0x73, 0x12, 0x52, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x74, 0x6c, + 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x69, 0x6e, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x12, 0x0b, 0x49, 0x6e, 0x73, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x54, 0x6c, 0x73, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, + 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x5f, 0x64, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x0a, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x44, 0x6e, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x65, 0x0a, 0x11, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, + 0x42, 0x37, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2f, 0x0a, 0x1c, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0f, 0x41, 0x6e, 0x6f, 0x6e, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x11, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x68, 0x0a, 0x0a, + 0x75, 0x70, 0x6e, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2a, + 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x22, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x70, 0x6e, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, + 0x09, 0x55, 0x70, 0x6e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x0a, 0x75, 0x70, 0x6e, 0x5f, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x46, + 0x20, 0x03, 0x28, 0x09, 0x42, 0x1f, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x17, 0x0a, 0x0f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x72, 0x6c, 0x73, 0x12, + 0x04, 0x55, 0x72, 0x6c, 0x73, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x06, 0x55, 0x73, 0x65, 0x72, 0x44, 0x6e, + 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x64, 0x0a, 0x09, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x28, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x20, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6c, 0x73, 0x12, 0x08, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x54, 0x6c, 0x73, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x6c, 0x73, 0x12, - 0x52, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x08, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, - 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x69, 0x6e, 0x73, 0x65, - 0x63, 0x75, 0x72, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x12, 0x0b, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, - 0x72, 0x65, 0x54, 0x6c, 0x73, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, - 0x74, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, - 0x64, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x24, 0x0a, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x0a, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x44, 0x6e, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x5f, 0x64, 0x6e, 0x12, 0x65, 0x0a, 0x11, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x42, 0x37, - 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2f, 0x0a, 0x1c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0f, 0x41, 0x6e, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x11, 0x61, 0x6e, 0x6f, 0x6e, 0x5f, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x68, 0x0a, 0x0a, 0x75, 0x70, - 0x6e, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2a, 0xa0, 0xda, - 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x22, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x2e, 0x75, 0x70, 0x6e, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x09, 0x55, - 0x70, 0x6e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x0a, 0x75, 0x70, 0x6e, 0x5f, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x46, 0x20, 0x03, - 0x28, 0x09, 0x42, 0x1f, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x17, 0x0a, 0x0f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x04, 0x55, - 0x72, 0x6c, 0x73, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x07, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x64, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x1c, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x06, 0x55, 0x73, 0x65, 0x72, 0x44, 0x6e, 0x52, 0x07, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6e, 0x12, 0x64, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x28, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x20, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, 0x08, 0x55, 0x73, 0x65, 0x72, 0x41, 0x74, - 0x74, 0x72, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, 0x6c, 0x0a, - 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x64, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x16, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x12, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0b, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x0d, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x6e, 0x20, 0x01, - 0x28, 0x08, 0x42, 0x30, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x18, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x0c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x73, 0x12, 0x60, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x64, 0x6e, 0x18, - 0x78, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x42, 0x26, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x13, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x64, 0x6e, 0x12, 0x07, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x6e, 0x52, 0x08, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x5f, 0x64, 0x6e, 0x12, 0x69, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x22, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x41, 0x74, 0x74, 0x72, 0x52, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x12, 0x71, 0x0a, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, 0x08, 0x55, 0x73, 0x65, 0x72, + 0x41, 0x74, 0x74, 0x72, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, + 0x6c, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x64, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x16, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x56, 0x0a, + 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x6e, + 0x20, 0x01, 0x28, 0x08, 0x42, 0x30, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x18, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x0c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x60, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x64, + 0x6e, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 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, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x66, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0c, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x2f, 0xa0, 0xda, 0x29, 0x01, - 0xc2, 0xdd, 0x29, 0x27, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x2e, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x0c, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x65, 0x72, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x89, 0x01, 0x0a, 0x12, 0x63, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x26, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, + 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x64, 0x6e, 0x12, 0x07, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x6e, 0x52, 0x08, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x64, 0x6e, 0x12, 0x69, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2a, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x22, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, 0x09, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x41, 0x74, 0x74, 0x72, 0x52, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x12, 0x71, 0x0a, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 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, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0b, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0c, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x2f, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x27, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x0c, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x0c, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x89, 0x01, 0x0a, 0x12, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x3a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x32, 0x0a, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x3a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x32, 0x0a, - 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x11, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x16, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x41, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x39, 0x0a, - 0x21, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x12, 0x11, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x16, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x16, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x65, 0x79, 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x41, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x39, 0x0a, 0x21, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x16, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x12, 0x41, 0x0a, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x6d, 0x61, + 0x63, 0x18, 0xb4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x12, 0x41, 0x0a, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, - 0xb4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, - 0x6d, 0x61, 0x63, 0x12, 0x5d, 0x0a, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x6e, 0x18, 0xbe, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x12, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x64, - 0x6e, 0x12, 0x06, 0x42, 0x69, 0x6e, 0x64, 0x44, 0x6e, 0x52, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x5f, - 0x64, 0x6e, 0x12, 0x75, 0x0a, 0x0d, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x30, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x28, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x62, - 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x0c, 0x42, 0x69, - 0x6e, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x0d, 0x62, 0x69, 0x6e, 0x64, - 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x12, 0x62, 0x69, 0x6e, - 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, - 0xd2, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x12, 0x62, 0x0a, 0x10, 0x75, 0x73, - 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0xdc, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x35, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2d, 0x0a, - 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x0e, 0x55, 0x73, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x10, 0x75, 0x73, - 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x7a, - 0x0a, 0x16, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0xe6, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, - 0x41, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x39, 0x0a, 0x21, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x12, 0x14, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4d, 0x61, - 0x70, 0x73, 0x52, 0x16, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x12, 0x66, 0x0a, 0x11, 0x6d, 0x61, - 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0xf0, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x37, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2f, - 0x0a, 0x1c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6d, 0x61, 0x78, - 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x0f, - 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x52, - 0x11, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0xfa, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x3c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x34, 0x0a, 0x1e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x12, 0x44, 0x65, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x13, 0x64, - 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x65, 0x73, 0x42, 0x60, 0xa2, 0xe3, 0x29, 0x04, 0x61, 0x75, 0x74, 0x68, 0x5a, 0x56, 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, 0x61, 0x75, 0x74, - 0x68, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3b, 0x61, 0x75, 0x74, 0x68, 0x6d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x12, 0x5d, 0x0a, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x6e, + 0x18, 0xbe, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, + 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x62, 0x69, 0x6e, 0x64, + 0x5f, 0x64, 0x6e, 0x12, 0x06, 0x42, 0x69, 0x6e, 0x64, 0x44, 0x6e, 0x52, 0x07, 0x62, 0x69, 0x6e, + 0x64, 0x5f, 0x64, 0x6e, 0x12, 0x75, 0x0a, 0x0d, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x30, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x0c, + 0x42, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x0d, 0x62, 0x69, + 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x12, 0x62, + 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, + 0x63, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x12, 0x62, 0x0a, 0x10, + 0x75, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x18, 0xdc, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x35, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x2d, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, + 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x0e, + 0x55, 0x73, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x10, + 0x75, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x12, 0x7a, 0x0a, 0x16, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0xe6, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x42, 0x41, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x39, 0x0a, 0x21, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x12, 0x14, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x4d, 0x61, 0x70, 0x73, 0x52, 0x16, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x12, 0x66, 0x0a, 0x11, + 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0xf0, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x37, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x2f, 0x0a, 0x1c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6d, + 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x0f, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, + 0x65, 0x52, 0x11, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0xfa, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x42, 0x3c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x34, 0x0a, 0x1e, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x12, 0x44, 0x65, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x13, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x42, 0x60, 0xa2, 0xe3, 0x29, 0x04, 0x61, 0x75, 0x74, 0x68, 0x5a, 0x56, + 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, 0x61, + 0x75, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3b, 0x61, 0x75, 0x74, 0x68, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var (