mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
Add OSS stub functions for Self-Managed Static Roles (#28199)
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
3
changelog/28199.txt
Normal file
3
changelog/28199.txt
Normal file
@@ -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). Requires Vault Enterprise.
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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,10 +242,19 @@ func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, ch
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
db, err := p.getConnection(ctx)
|
||||
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
|
||||
var exists bool
|
||||
@@ -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)
|
||||
var db *sql.DB
|
||||
var err error
|
||||
if selfManagedPass == "" {
|
||||
db, err = p.getConnection(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
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)
|
||||
|
||||
@@ -640,6 +640,94 @@ func TestPostgreSQL_Initialize_CloudGCP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestPostgreSQL_Initialize_SelfManaged_OSS tests the initialization of
|
||||
// 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()
|
||||
|
||||
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,37 @@ 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)
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -204,6 +204,7 @@ func updateUserReqToProto(req UpdateUserRequest) (*proto.UpdateUserRequest, erro
|
||||
Password: password,
|
||||
PublicKey: publicKey,
|
||||
Expiration: expiration,
|
||||
SelfManagedPassword: req.SelfManagedPassword,
|
||||
}
|
||||
return rpcReq, nil
|
||||
}
|
||||
|
||||
@@ -227,6 +227,7 @@ func getUpdateUserRequest(req *proto.UpdateUserRequest) (UpdateUserRequest, erro
|
||||
Password: password,
|
||||
PublicKey: publicKey,
|
||||
Expiration: expiration,
|
||||
SelfManagedPassword: req.SelfManagedPassword,
|
||||
}
|
||||
|
||||
if !hasChange(dbReq) {
|
||||
|
||||
@@ -348,6 +348,7 @@ type UpdateUserRequest struct {
|
||||
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 (
|
||||
|
||||
@@ -53,6 +53,7 @@ message UpdateUserRequest {
|
||||
ChangeExpiration expiration = 3;
|
||||
ChangePublicKey public_key = 4;
|
||||
int32 credential_type = 5;
|
||||
string self_managed_password = 6;
|
||||
}
|
||||
|
||||
message ChangePassword {
|
||||
|
||||
16
sdk/database/helper/cacheutil/cache_stubs_oss.go
Normal file
16
sdk/database/helper/cacheutil/cache_stubs_oss.go
Normal file
@@ -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")
|
||||
}
|
||||
@@ -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,11 +114,28 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf
|
||||
return nil, fmt.Errorf("username and/or password cannot contain the template variables")
|
||||
}
|
||||
|
||||
// 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'")
|
||||
}
|
||||
|
||||
// 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
|
||||
username := c.Username
|
||||
password := c.Password
|
||||
if !c.DisableEscaping {
|
||||
username = url.PathEscape(c.Username)
|
||||
}
|
||||
@@ -111,6 +143,13 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf
|
||||
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
|
||||
// then maybe we won't be able to use it to do URL substitution any more.
|
||||
c.ConnectionURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{
|
||||
@@ -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,
|
||||
|
||||
16
sdk/database/helper/connutil/sql_stubs_oss.go
Normal file
16
sdk/database/helper/connutil/sql_stubs_oss.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package connutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (c *SQLConnectionProducer) StaticConnection(_ context.Context, _, _ string) (*sql.DB, error) {
|
||||
return nil, errors.New("self-managed static roles only available in Vault Enterprise")
|
||||
}
|
||||
Reference in New Issue
Block a user