From c161ee38481376c1f7bf2c808f94b3800e65141f Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Tue, 27 Aug 2024 10:02:20 -0700 Subject: [PATCH 1/6] add oss stub for static connections --- sdk/database/dbplugin/v5/database.go | 7 +++++++ sdk/database/helper/connutil/sql_stubs_oss.go | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 sdk/database/helper/connutil/sql_stubs_oss.go diff --git a/sdk/database/dbplugin/v5/database.go b/sdk/database/dbplugin/v5/database.go index ddbcb6c81bbc..2943451b7212 100644 --- a/sdk/database/dbplugin/v5/database.go +++ b/sdk/database/dbplugin/v5/database.go @@ -181,6 +181,13 @@ type UpdateUserRequest struct { // Expiration indicates the new expiration date to change to. // If nil, no change is requested. Expiration *ChangeExpiration + + // SelfManagedPassword is the password for an externally managed user in the DB. + // If this field is supplied, a DB connection is retrieved from the static + // account cache for the particular DB plugin and used to update the password of + // the self-managed static role. + // *ENTERPRISE-ONLY* + SelfManagedPassword string } // ChangePublicKey of a given user diff --git a/sdk/database/helper/connutil/sql_stubs_oss.go b/sdk/database/helper/connutil/sql_stubs_oss.go new file mode 100644 index 000000000000..726ac384b025 --- /dev/null +++ b/sdk/database/helper/connutil/sql_stubs_oss.go @@ -0,0 +1,18 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !enterprise + +package connutil + +import ( + "context" + "database/sql" + "errors" +) + +//go:generate go run github.com/hashicorp/vault/tools/stubmaker + +func (c *SQLConnectionProducer) StaticConnection(_ context.Context, _, _ string) (*sql.DB, error) { + return nil, errors.New("self-managed static roles not implemented in CE") +} From 9d084ea2d6be8636c87b132c6b3981136ae69d03 Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Tue, 27 Aug 2024 10:11:23 -0700 Subject: [PATCH 2/6] add changelog --- changelog/28199.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/28199.txt diff --git a/changelog/28199.txt b/changelog/28199.txt new file mode 100644 index 000000000000..674d6cb36b26 --- /dev/null +++ b/changelog/28199.txt @@ -0,0 +1,3 @@ +```release-note:feature +**Self-Managed Static Roles**: Self-Managed Static Roles are now supported for select SQL database engines (Postgres, Oracle). +``` \ No newline at end of file From e12deecc850abac8831bcef646422f8cbf226d81 Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Tue, 27 Aug 2024 10:14:35 -0700 Subject: [PATCH 3/6] add enterprise note in changelog --- changelog/28199.txt | 2 +- sdk/database/helper/connutil/sql_stubs_oss.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/28199.txt b/changelog/28199.txt index 674d6cb36b26..9b94d97f8c5f 100644 --- a/changelog/28199.txt +++ b/changelog/28199.txt @@ -1,3 +1,3 @@ ```release-note:feature -**Self-Managed Static Roles**: Self-Managed Static Roles are now supported for select SQL database engines (Postgres, Oracle). +**Self-Managed Static Roles**: Self-Managed Static Roles are now supported for select SQL database engines (Postgres, Oracle). Requires Vault Enterprise. ``` \ No newline at end of file diff --git a/sdk/database/helper/connutil/sql_stubs_oss.go b/sdk/database/helper/connutil/sql_stubs_oss.go index 726ac384b025..d671fc9b5770 100644 --- a/sdk/database/helper/connutil/sql_stubs_oss.go +++ b/sdk/database/helper/connutil/sql_stubs_oss.go @@ -1,5 +1,5 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 //go:build !enterprise From 25351df2b6bebde9d189adbffeb0f61c2072baf1 Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Wed, 28 Aug 2024 10:44:51 -0700 Subject: [PATCH 4/6] fix stubmaker errors --- sdk/database/helper/connutil/sql_stubs_oss.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/database/helper/connutil/sql_stubs_oss.go b/sdk/database/helper/connutil/sql_stubs_oss.go index d671fc9b5770..a4f2690de1f1 100644 --- a/sdk/database/helper/connutil/sql_stubs_oss.go +++ b/sdk/database/helper/connutil/sql_stubs_oss.go @@ -11,8 +11,6 @@ import ( "errors" ) -//go:generate go run github.com/hashicorp/vault/tools/stubmaker - func (c *SQLConnectionProducer) StaticConnection(_ context.Context, _, _ string) (*sql.DB, error) { return nil, errors.New("self-managed static roles not implemented in CE") } From ed8d9c9424c7834447066aa91ba96885ad8a8783 Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Wed, 28 Aug 2024 15:00:29 -0700 Subject: [PATCH 5/6] add all relevant stubs from Ent PR to make diff happy --- builtin/credential/aws/client.go | 3 +- builtin/credential/cert/path_login.go | 3 +- builtin/logical/database/path_roles.go | 15 ++ builtin/logical/database/rotation.go | 11 ++ .../postgresql/postgresqlhelper.go | 23 +++ plugins/database/postgresql/postgresql.go | 47 +++-- .../database/postgresql/postgresql_test.go | 117 +++++++++++++ sdk/database/dbplugin/v5/conversions_test.go | 2 + sdk/database/dbplugin/v5/grpc_client.go | 11 +- sdk/database/dbplugin/v5/grpc_server.go | 11 +- sdk/database/dbplugin/v5/proto/database.pb.go | 163 ++++++++++-------- sdk/database/dbplugin/v5/proto/database.proto | 1 + .../helper/cacheutil/cache_stubs_oss.go | 16 ++ sdk/database/helper/connutil/sql.go | 120 ++++++++++--- sdk/database/helper/connutil/sql_stubs_oss.go | 2 +- 15 files changed, 418 insertions(+), 127 deletions(-) create mode 100644 sdk/database/helper/cacheutil/cache_stubs_oss.go diff --git a/builtin/credential/aws/client.go b/builtin/credential/aws/client.go index 5931984ca743..d06900b9f123 100644 --- a/builtin/credential/aws/client.go +++ b/builtin/credential/aws/client.go @@ -9,9 +9,8 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" diff --git a/builtin/credential/cert/path_login.go b/builtin/credential/cert/path_login.go index d0476118e330..832036f12dc0 100644 --- a/builtin/credential/cert/path_login.go +++ b/builtin/credential/cert/path_login.go @@ -17,13 +17,12 @@ import ( "net/url" "strings" - "github.com/hashicorp/vault/sdk/helper/locksutil" - "github.com/hashicorp/errwrap" "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/certutil" "github.com/hashicorp/vault/sdk/helper/cidrutil" + "github.com/hashicorp/vault/sdk/helper/locksutil" "github.com/hashicorp/vault/sdk/helper/ocsp" "github.com/hashicorp/vault/sdk/helper/policyutil" "github.com/hashicorp/vault/sdk/logical" diff --git a/builtin/logical/database/path_roles.go b/builtin/logical/database/path_roles.go index e223018962fd..a53988498000 100644 --- a/builtin/logical/database/path_roles.go +++ b/builtin/logical/database/path_roles.go @@ -217,6 +217,11 @@ func staticFields() map[string]*framework.FieldSchema { this functionality. See the plugin's API page for more information on support and formatting for this parameter.`, }, + "self_managed_password": { + Type: framework.TypeString, + Description: `Used to connect to a self-managed static account. Must + be provided by the user when root credentials are not provided.`, + }, } return fields } @@ -628,6 +633,10 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l } } + if smPasswordRaw, ok := data.GetOk("self_managed_password"); ok && createRole { + role.StaticAccount.SelfManagedPassword = smPasswordRaw.(string) + } + var credentialConfig map[string]string if raw, ok := data.GetOk("credential_config"); ok { credentialConfig = raw.(map[string]string) @@ -785,6 +794,12 @@ type staticAccount struct { // Username to create or assume management for static accounts Username string `json:"username"` + // SelfManagedPassword is used to make a dedicated connection to the DB + // user specified by Username. The credentials will leverage the existing + // static role mechanisms to handle password rotations. Required when root + // credentials are not provided. + SelfManagedPassword string `json:"self_managed_password"` + // Password is the current password credential for static accounts. As an input, // this is used/required when trying to assume management of an existing static // account. Returned on credential request if the role's credential type is diff --git a/builtin/logical/database/rotation.go b/builtin/logical/database/rotation.go index 0e5840bd30c2..d4d41cf570b5 100644 --- a/builtin/logical/database/rotation.go +++ b/builtin/logical/database/rotation.go @@ -413,6 +413,11 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag Commands: input.Role.Statements.Rotation, } + // Add external password to request so we can use static account connection + if input.Role.StaticAccount.SelfManagedPassword != "" { + updateReq.SelfManagedPassword = input.Role.StaticAccount.SelfManagedPassword + } + // Use credential from input if available. This happens if we're restoring from // a WAL item or processing the rotation queue with an item that has a WAL // associated with it @@ -529,6 +534,12 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag } modified = true + // static user password successfully updated in external system + // update self-managed password if available for future connections + if input.Role.StaticAccount.SelfManagedPassword != "" { + input.Role.StaticAccount.SelfManagedPassword = input.Role.StaticAccount.Password + } + // Store updated role information // lvr is the known LastVaultRotation lvr := time.Now() diff --git a/helper/testhelpers/postgresql/postgresqlhelper.go b/helper/testhelpers/postgresql/postgresqlhelper.go index 7229d2127b9f..c6ced3190b25 100644 --- a/helper/testhelpers/postgresql/postgresqlhelper.go +++ b/helper/testhelpers/postgresql/postgresqlhelper.go @@ -50,6 +50,10 @@ func PrepareTestContainer(t *testing.T) (func(), string) { return cleanup, url } +func PrepareTestContainerSelfManaged(t *testing.T) (func(), *url.URL) { + return prepareTestContainerSelfManaged(t, defaultRunOpts(t), defaultPGPass, true, false, false) +} + func PrepareTestContainerMultiHost(t *testing.T) (func(), string) { _, cleanup, url, _ := prepareTestContainer(t, defaultRunOpts(t), defaultPGPass, true, false, true) @@ -198,6 +202,25 @@ func prepareTestContainer(t *testing.T, runOpts docker.RunOptions, password stri return runner, svc.Cleanup, svc.Config.URL().String(), containerID } +func prepareTestContainerSelfManaged(t *testing.T, runOpts docker.RunOptions, password string, addSuffix, forceLocalAddr, useFallback bool, +) (func(), *url.URL) { + if os.Getenv("PG_URL") != "" { + return func() {}, nil + } + + runner, err := docker.NewServiceRunner(runOpts) + if err != nil { + t.Fatalf("Could not start docker Postgres: %s", err) + } + + svc, _, err := runner.StartNewService(context.Background(), addSuffix, forceLocalAddr, connectPostgres(password, runOpts.ImageRepo, useFallback)) + if err != nil { + t.Fatalf("Could not start docker Postgres: %s", err) + } + + return svc.Cleanup, svc.Config.URL() +} + func getPostgresSSLConfig(t *testing.T, host, sslMode, caCert, clientCert, clientKey string, useFallback bool) docker.ServiceConfig { if useFallback { // set the first host to a bad address so we can test the fallback logic diff --git a/plugins/database/postgresql/postgresql.go b/plugins/database/postgresql/postgresql.go index 3a9bf7ae7c2a..004ca27dfc2b 100644 --- a/plugins/database/postgresql/postgresql.go +++ b/plugins/database/postgresql/postgresql.go @@ -199,6 +199,15 @@ func (p *PostgreSQL) getConnection(ctx context.Context) (*sql.DB, error) { return db.(*sql.DB), nil } +func (p *PostgreSQL) getStaticConnection(ctx context.Context, username, password string) (*sql.DB, error) { + db, err := p.StaticConnection(ctx, username, password) + if err != nil { + return nil, err + } + + return db, nil +} + func (p *PostgreSQL) UpdateUser(ctx context.Context, req dbplugin.UpdateUserRequest) (dbplugin.UpdateUserResponse, error) { if req.Username == "" { return dbplugin.UpdateUserResponse{}, fmt.Errorf("missing username") @@ -209,17 +218,17 @@ func (p *PostgreSQL) UpdateUser(ctx context.Context, req dbplugin.UpdateUserRequ merr := &multierror.Error{} if req.Password != nil { - err := p.changeUserPassword(ctx, req.Username, req.Password) + err := p.changeUserPassword(ctx, req.Username, req.Password, req.SelfManagedPassword) merr = multierror.Append(merr, err) } if req.Expiration != nil { - err := p.changeUserExpiration(ctx, req.Username, req.Expiration) + err := p.changeUserExpiration(ctx, req.Username, req.Expiration, req.SelfManagedPassword) merr = multierror.Append(merr, err) } return dbplugin.UpdateUserResponse{}, merr.ErrorOrNil() } -func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, changePass *dbplugin.ChangePassword) error { +func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, changePass *dbplugin.ChangePassword, selfManagedPass string) error { stmts := changePass.Statements.Commands if len(stmts) == 0 { stmts = []string{defaultChangePasswordStatement} @@ -233,9 +242,18 @@ func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, ch p.Lock() defer p.Unlock() - db, err := p.getConnection(ctx) - if err != nil { - return fmt.Errorf("unable to get connection: %w", err) + var db *sql.DB + var err error + if selfManagedPass == "" { + db, err = p.getConnection(ctx) + if err != nil { + return fmt.Errorf("unable to get connection: %w", err) + } + } else { + db, err = p.getStaticConnection(ctx, username, selfManagedPass) + if err != nil { + return fmt.Errorf("unable to get static connection from cache: %w", err) + } } // Check if the role exists @@ -285,7 +303,7 @@ func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, ch return nil } -func (p *PostgreSQL) changeUserExpiration(ctx context.Context, username string, changeExp *dbplugin.ChangeExpiration) error { +func (p *PostgreSQL) changeUserExpiration(ctx context.Context, username string, changeExp *dbplugin.ChangeExpiration, selfManagedPass string) error { p.Lock() defer p.Unlock() @@ -294,9 +312,18 @@ func (p *PostgreSQL) changeUserExpiration(ctx context.Context, username string, renewStmts = []string{defaultExpirationStatement} } - db, err := p.getConnection(ctx) - if err != nil { - return err + var db *sql.DB + var err error + if selfManagedPass == "" { + db, err = p.getConnection(ctx) + if err != nil { + return fmt.Errorf("unable to get connection: %w", err) + } + } else { + db, err = p.getStaticConnection(ctx, username, selfManagedPass) + if err != nil { + return fmt.Errorf("unable to get static connection from cache: %w", err) + } } tx, err := db.BeginTx(ctx, nil) diff --git a/plugins/database/postgresql/postgresql_test.go b/plugins/database/postgresql/postgresql_test.go index ba150870c7bb..c16aede2d6e3 100644 --- a/plugins/database/postgresql/postgresql_test.go +++ b/plugins/database/postgresql/postgresql_test.go @@ -640,6 +640,94 @@ func TestPostgreSQL_Initialize_CloudGCP(t *testing.T) { } } +// TestPostgreSQL_Initialize_SelfManaged_OSS tests the initialization of +// the self-managed flow. +func TestPostgreSQL_Initialize_SelfManaged_OSS(t *testing.T) { + cleanup, url := postgresql.PrepareTestContainerSelfManaged(t) + defer cleanup() + + connURL := fmt.Sprintf("postgresql://{{username}}:{{password}}@%s/postgres?sslmode=disable", url.Host) + + testCases := []struct { + name string + connectionDetails map[string]interface{} + wantErr bool + errContains string + }{ + { + name: "no parameters set", + connectionDetails: map[string]interface{}{ + "connection_url": connURL, + "self_managed": false, + "username": "", + "password": "", + }, + wantErr: true, + errContains: "must either provide username/password or set self-managed to 'true'", + }, + { + name: "both sets of parameters set", + connectionDetails: map[string]interface{}{ + "connection_url": connURL, + "self_managed": true, + "username": "test", + "password": "test", + }, + wantErr: true, + errContains: "cannot use both self-managed and vault-managed workflows", + }, + { + name: "either username/password with self-managed", + connectionDetails: map[string]interface{}{ + "connection_url": connURL, + "self_managed": true, + "username": "test", + "password": "", + }, + wantErr: true, + errContains: "cannot use both self-managed and vault-managed workflows", + }, + { + name: "cache not implemented", + connectionDetails: map[string]interface{}{ + "connection_url": connURL, + "self_managed": true, + "username": "", + "password": "", + }, + wantErr: true, + errContains: "self-managed static roles only available in Vault Enterprise", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + req := dbplugin.InitializeRequest{ + Config: tc.connectionDetails, + VerifyConnection: true, + } + + db := new() + _, err := dbtesting.VerifyInitialize(t, db, req) + if err == nil && tc.wantErr { + t.Fatalf("got: %s, wantErr: %t", err, tc.wantErr) + } + + if err != nil && !strings.Contains(err.Error(), tc.errContains) { + t.Fatalf("expected error: %s, received error: %s", tc.errContains, err) + } + + if !tc.wantErr && !db.Initialized { + t.Fatal("Database should be initialized") + } + + if err := db.Close(); err != nil { + t.Fatalf("err closing DB: %s", err) + } + }) + } +} + // TestPostgreSQL_PasswordAuthentication tests that the default "password_authentication" is "none", and that // an error is returned if an invalid "password_authentication" is provided. func TestPostgreSQL_PasswordAuthentication(t *testing.T) { @@ -1045,6 +1133,35 @@ func TestUpdateUser_Password(t *testing.T) { }) } +func TestUpdateUser_SelfManaged_OSS(t *testing.T) { + // Shared test container for speed - there should not be any overlap between the tests + db, cleanup := getPostgreSQL(t, nil) + defer cleanup() + + updateReq := dbplugin.UpdateUserRequest{ + Username: "static", + Password: &dbplugin.ChangePassword{ + NewPassword: "somenewpassword", + Statements: dbplugin.Statements{ + Commands: nil, + }, + }, + SelfManagedPassword: "test", + } + + expectedErr := "self-managed static roles only available in Vault Enterprise" + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _, err := db.UpdateUser(ctx, updateReq) + if err == nil { + t.Fatalf("err expected, got nil") + } + if !strings.Contains(err.Error(), expectedErr) { + t.Fatalf("err expected: %s, got: %s", expectedErr, err) + } +} + func TestUpdateUser_Expiration(t *testing.T) { type testCase struct { initialExpiration time.Time diff --git a/sdk/database/dbplugin/v5/conversions_test.go b/sdk/database/dbplugin/v5/conversions_test.go index 5e65c3467068..c6b805bb9ff7 100644 --- a/sdk/database/dbplugin/v5/conversions_test.go +++ b/sdk/database/dbplugin/v5/conversions_test.go @@ -116,6 +116,7 @@ func TestConversionsHaveAllFields(t *testing.T) { }, }, }, + SelfManagedPassword: "test-password", } protoReq, err := updateUserReqToProto(req) @@ -194,6 +195,7 @@ func TestConversionsHaveAllFields(t *testing.T) { }, }, }, + SelfManagedPassword: "test-password", } protoReq, err := getUpdateUserRequest(req) diff --git a/sdk/database/dbplugin/v5/grpc_client.go b/sdk/database/dbplugin/v5/grpc_client.go index 9b0b984f42af..4e1ef57492a9 100644 --- a/sdk/database/dbplugin/v5/grpc_client.go +++ b/sdk/database/dbplugin/v5/grpc_client.go @@ -199,11 +199,12 @@ func updateUserReqToProto(req UpdateUserRequest) (*proto.UpdateUserRequest, erro } rpcReq := &proto.UpdateUserRequest{ - Username: req.Username, - CredentialType: int32(req.CredentialType), - Password: password, - PublicKey: publicKey, - Expiration: expiration, + Username: req.Username, + CredentialType: int32(req.CredentialType), + Password: password, + PublicKey: publicKey, + Expiration: expiration, + SelfManagedPassword: req.SelfManagedPassword, } return rpcReq, nil } diff --git a/sdk/database/dbplugin/v5/grpc_server.go b/sdk/database/dbplugin/v5/grpc_server.go index 7e1bc3fa1fc7..691de2d0a89e 100644 --- a/sdk/database/dbplugin/v5/grpc_server.go +++ b/sdk/database/dbplugin/v5/grpc_server.go @@ -222,11 +222,12 @@ func getUpdateUserRequest(req *proto.UpdateUserRequest) (UpdateUserRequest, erro } dbReq := UpdateUserRequest{ - Username: req.GetUsername(), - CredentialType: CredentialType(req.GetCredentialType()), - Password: password, - PublicKey: publicKey, - Expiration: expiration, + Username: req.GetUsername(), + CredentialType: CredentialType(req.GetCredentialType()), + Password: password, + PublicKey: publicKey, + Expiration: expiration, + SelfManagedPassword: req.SelfManagedPassword, } if !hasChange(dbReq) { diff --git a/sdk/database/dbplugin/v5/proto/database.pb.go b/sdk/database/dbplugin/v5/proto/database.pb.go index 745bd853342d..376315758927 100644 --- a/sdk/database/dbplugin/v5/proto/database.pb.go +++ b/sdk/database/dbplugin/v5/proto/database.pb.go @@ -343,11 +343,12 @@ type UpdateUserRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - Password *ChangePassword `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` - Expiration *ChangeExpiration `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` - PublicKey *ChangePublicKey `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - CredentialType int32 `protobuf:"varint,5,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password *ChangePassword `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Expiration *ChangeExpiration `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` + PublicKey *ChangePublicKey `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + CredentialType int32 `protobuf:"varint,5,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty"` + SelfManagedPassword string `protobuf:"bytes,6,opt,name=self_managed_password,json=selfManagedPassword,proto3" json:"self_managed_password,omitempty"` } func (x *UpdateUserRequest) Reset() { @@ -417,6 +418,13 @@ func (x *UpdateUserRequest) GetCredentialType() int32 { return 0 } +func (x *UpdateUserRequest) GetSelfManagedPassword() string { + if x != nil { + return x.SelfManagedPassword + } + return "" +} + type ChangePassword struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -910,7 +918,7 @@ var file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc = []byte{ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8d, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc1, 0x02, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, @@ -927,76 +935,79 @@ var file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc = []byte{ 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6c, - 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x70, 0x0a, 0x0f, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, - 0x24, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x77, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x62, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8e, - 0x01, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x65, 0x77, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x62, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x14, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x68, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x62, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x22, 0x28, 0x0a, 0x0a, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0xa5, 0x03, 0x0a, - 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x49, 0x6e, 0x69, - 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x55, - 0x73, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, - 0x35, 0x2e, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x4e, - 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, - 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x64, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, - 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x64, 0x62, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x62, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x76, 0x35, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x12, 0x2e, 0x64, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x12, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, - 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2f, - 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x32, + 0x0a, 0x15, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x73, + 0x65, 0x6c, 0x66, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x22, 0x6c, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x70, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x77, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x5f, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x65, 0x77, + 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x68, 0x0a, 0x11, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x22, 0x28, 0x0a, + 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x32, 0xa5, 0x03, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x4d, 0x0a, + 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x2e, 0x64, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, + 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x35, 0x2e, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, + 0x12, 0x1e, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x1e, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x35, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x64, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, + 0x12, 0x12, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x76, 0x35, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, + 0x61, 0x73, 0x65, 0x2f, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x76, 0x35, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/database/dbplugin/v5/proto/database.proto b/sdk/database/dbplugin/v5/proto/database.proto index 2b0ebde48574..c30984ef758b 100644 --- a/sdk/database/dbplugin/v5/proto/database.proto +++ b/sdk/database/dbplugin/v5/proto/database.proto @@ -53,6 +53,7 @@ message UpdateUserRequest { ChangeExpiration expiration = 3; ChangePublicKey public_key = 4; int32 credential_type = 5; + string self_managed_password = 6; } message ChangePassword { diff --git a/sdk/database/helper/cacheutil/cache_stubs_oss.go b/sdk/database/helper/cacheutil/cache_stubs_oss.go new file mode 100644 index 000000000000..18e141832fcc --- /dev/null +++ b/sdk/database/helper/cacheutil/cache_stubs_oss.go @@ -0,0 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build !enterprise + +package cacheutil + +import "errors" + +type EvictionFunc func(key interface{}, value interface{}) + +type Cache struct{} + +func NewCache(_ int, _ EvictionFunc) (*Cache, error) { + return nil, errors.New("self-managed static roles only available in Vault Enterprise") +} diff --git a/sdk/database/helper/connutil/sql.go b/sdk/database/helper/connutil/sql.go index bd19e77f6be5..489becf0c113 100644 --- a/sdk/database/helper/connutil/sql.go +++ b/sdk/database/helper/connutil/sql.go @@ -15,9 +15,11 @@ import ( "time" "github.com/hashicorp/errwrap" + log "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/helper/cacheutil" "github.com/hashicorp/vault/sdk/database/helper/dbutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/jackc/pgx/v4" @@ -34,6 +36,12 @@ const ( const ( dbTypePostgres = "pgx" cloudSQLPostgres = "cloudsql-postgres" + + // controls the size of the static account cache + // as part of the self-managed workflow + defaultStaticCacheSize = 4 + defaultSelfManagedUsername = "self-managed-user" + defaultSelfManagedPassword = "self-managed-password" ) var _ ConnectionProducer = &SQLConnectionProducer{} @@ -46,6 +54,7 @@ type SQLConnectionProducer struct { MaxConnectionLifetimeRaw interface{} `json:"max_connection_lifetime" mapstructure:"max_connection_lifetime" structs:"max_connection_lifetime"` DisableEscaping bool `json:"disable_escaping" mapstructure:"disable_escaping" structs:"disable_escaping"` usePrivateIP bool `json:"use_private_ip" mapstructure:"use_private_ip" structs:"use_private_ip"` + SelfManaged bool `json:"self_managed" mapstructure:"self_managed" structs:"self_managed"` // Username/Password is the default auth type when AuthType is not set Username string `json:"username" mapstructure:"username" structs:"username"` @@ -66,6 +75,7 @@ type SQLConnectionProducer struct { maxConnectionLifetime time.Duration Initialized bool db *sql.DB + staticAccountsCache *cacheutil.Cache sync.Mutex } @@ -89,6 +99,11 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf return nil, fmt.Errorf("connection_url cannot be empty") } + isTemplatedURL := true + if !strings.Contains(c.ConnectionURL, "{{username}}") || !strings.Contains(c.ConnectionURL, "{{password}}") { + isTemplatedURL = false + } + // Do not allow the username or password template pattern to be used as // part of the user-supplied username or password if strings.Contains(c.Username, "{{username}}") || @@ -99,16 +114,40 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf return nil, fmt.Errorf("username and/or password cannot contain the template variables") } - // Don't escape special characters for MySQL password - // Also don't escape special characters for the username and password if - // the disable_escaping parameter is set to true - username := c.Username - password := c.Password - if !c.DisableEscaping { - username = url.PathEscape(c.Username) + // validate that at least one of username/password / self_managed is set + if !c.SelfManaged && (c.Username == "" && c.Password == "") && isTemplatedURL { + return nil, fmt.Errorf("must either provide username/password or set self-managed to 'true'") } - if (c.Type != "mysql") && !c.DisableEscaping { - password = url.PathEscape(c.Password) + + // validate that self-managed and username/password are mutually exclusive + if c.SelfManaged { + if (c.Username != "" || c.Password != "") || !isTemplatedURL { + return nil, fmt.Errorf("cannot use both self-managed and vault-managed workflows") + } + } + + var username string + var password string + if !c.SelfManaged { + // Default behavior + username = c.Username + password = c.Password + + // Don't escape special characters for MySQL password + // Also don't escape special characters for the username and password if + // the disable_escaping parameter is set to true + if !c.DisableEscaping { + username = url.PathEscape(c.Username) + } + if (c.Type != "mysql") && !c.DisableEscaping { + password = url.PathEscape(c.Password) + } + + } else { + // this is added to make middleware happy + // these placeholders are replaced when we make the actual static connection + username = defaultSelfManagedUsername + password = defaultSelfManagedPassword } // QueryHelper doesn't do any SQL escaping, but if it starts to do so @@ -159,11 +198,35 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf c.cloudDialerCleanup = dialerCleanup } + if c.SelfManaged && c.staticAccountsCache == nil { + logger := log.New(&log.LoggerOptions{ + Level: log.Trace, + }) + + closer := func(key interface{}, value interface{}) { + logger.Trace(fmt.Sprintf("Evicting key %s from static LRU cache", key)) + conn, ok := value.(*sql.DB) + if !ok { + logger.Error(fmt.Sprintf("error retrieving connection %s from static LRU cache, err=%s", key, err)) + } + + if err := conn.Close(); err != nil { + logger.Error(fmt.Sprintf("error closing connection for %s, err=%s", key, err)) + } + logger.Trace(fmt.Sprintf("closed DB connection for %s", key)) + } + c.staticAccountsCache, err = cacheutil.NewCache(defaultStaticCacheSize, closer) + if err != nil { + return nil, fmt.Errorf("error initializing static account cache: %s", err) + } + } + // Set initialized to true at this point since all fields are set, // and the connection can be established at a later time. c.Initialized = true - if verifyConnection { + // only verify if not self-managed + if verifyConnection && !c.SelfManaged { if _, err := c.Connection(ctx); err != nil { return nil, errwrap.Wrapf("error verifying connection: {{err}}", err) } @@ -210,22 +273,8 @@ func (c *SQLConnectionProducer) Connection(ctx context.Context) (interface{}, er } // Otherwise, attempt to make connection - conn := c.ConnectionURL - - // PostgreSQL specific settings - if strings.HasPrefix(conn, "postgres://") || strings.HasPrefix(conn, "postgresql://") { - // Ensure timezone is set to UTC for all the connections - if strings.Contains(conn, "?") { - conn += "&timezone=UTC" - } else { - conn += "?timezone=UTC" - } - - // Ensure a reasonable application_name is set - if !strings.Contains(conn, "application_name") { - conn += "&application_name=vault" - } - } + // Apply PostgreSQL specific settings if needed + conn := applyPostgresSettings(c.ConnectionURL) if driverName == dbTypePostgres && c.TLSConfig != nil { config, err := pgx.ParseConfig(conn) @@ -312,6 +361,25 @@ func (c *SQLConnectionProducer) SetCredentials(ctx context.Context, statements d return "", "", dbutil.Unimplemented() } +func applyPostgresSettings(connURL string) string { + res := connURL + if strings.HasPrefix(res, "postgres://") || strings.HasPrefix(res, "postgresql://") { + // Ensure timezone is set to UTC for all the connections + if strings.Contains(res, "?") { + res += "&timezone=UTC" + } else { + res += "?timezone=UTC" + } + + // Ensure a reasonable application_name is set + if !strings.Contains(res, "application_name") { + res += "&application_name=vault" + } + } + + return res +} + var configurableAuthTypes = map[string]bool{ AuthTypeUsernamePassword: true, AuthTypeCert: true, diff --git a/sdk/database/helper/connutil/sql_stubs_oss.go b/sdk/database/helper/connutil/sql_stubs_oss.go index a4f2690de1f1..b8ab85bec412 100644 --- a/sdk/database/helper/connutil/sql_stubs_oss.go +++ b/sdk/database/helper/connutil/sql_stubs_oss.go @@ -12,5 +12,5 @@ import ( ) func (c *SQLConnectionProducer) StaticConnection(_ context.Context, _, _ string) (*sql.DB, error) { - return nil, errors.New("self-managed static roles not implemented in CE") + return nil, errors.New("self-managed static roles only available in Vault Enterprise") } From 391697aa9be92f4e58d020b464212306f492b0be Mon Sep 17 00:00:00 2001 From: Vinay Gopalan Date: Thu, 29 Aug 2024 09:24:19 -0700 Subject: [PATCH 6/6] add doc string for test --- plugins/database/postgresql/postgresql_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/database/postgresql/postgresql_test.go b/plugins/database/postgresql/postgresql_test.go index c16aede2d6e3..dc6fb08c095d 100644 --- a/plugins/database/postgresql/postgresql_test.go +++ b/plugins/database/postgresql/postgresql_test.go @@ -641,7 +641,7 @@ func TestPostgreSQL_Initialize_CloudGCP(t *testing.T) { } // TestPostgreSQL_Initialize_SelfManaged_OSS tests the initialization of -// the self-managed flow. +// the self-managed flow and ensures an error is returned on OSS. func TestPostgreSQL_Initialize_SelfManaged_OSS(t *testing.T) { cleanup, url := postgresql.PrepareTestContainerSelfManaged(t) defer cleanup() @@ -1133,6 +1133,8 @@ func TestUpdateUser_Password(t *testing.T) { }) } +// TestUpdateUser_SelfManaged_OSS checks basic validation +// for self-managed fields and confirms an error is returned on OSS func TestUpdateUser_SelfManaged_OSS(t *testing.T) { // Shared test container for speed - there should not be any overlap between the tests db, cleanup := getPostgreSQL(t, nil)