secrets/database: adds ability to manage alternative credential types and configuration (#15376)

This commit is contained in:
Austin Gebauer 2022-05-17 09:21:26 -07:00 committed by GitHub
parent 60acf9ad6e
commit d3629ab49d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1627 additions and 400 deletions

View File

@ -0,0 +1,169 @@
package database
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"strings"
"github.com/hashicorp/vault/helper/random"
"github.com/mitchellh/mapstructure"
)
// passwordGenerator generates password credentials.
// A zero value passwordGenerator is usable.
type passwordGenerator struct {
// PasswordPolicy is the named password policy used to generate passwords.
// If empty (default), a random string of 20 characters will be generated.
PasswordPolicy string `mapstructure:"password_policy,omitempty"`
}
// newPasswordGenerator returns a new passwordGenerator using the given config.
// Default values will be set on the returned passwordGenerator if not provided
// in the config.
func newPasswordGenerator(config map[string]interface{}) (passwordGenerator, error) {
var pg passwordGenerator
if err := mapstructure.WeakDecode(config, &pg); err != nil {
return pg, err
}
return pg, nil
}
// Generate generates a password credential using the configured password policy.
// Returns the generated password or an error.
func (pg passwordGenerator) generate(ctx context.Context, b *databaseBackend, wrapper databaseVersionWrapper) (string, error) {
if !wrapper.isV5() && !wrapper.isV4() {
return "", fmt.Errorf("no underlying database specified")
}
// The database plugin generates the password if its interface is v4
if wrapper.isV4() {
password, err := wrapper.v4.GenerateCredentials(ctx)
if err != nil {
return "", err
}
return password, nil
}
if pg.PasswordPolicy == "" {
return random.DefaultStringGenerator.Generate(ctx, b.GetRandomReader())
}
return b.System().GeneratePasswordFromPolicy(ctx, pg.PasswordPolicy)
}
// configMap returns the configuration of the passwordGenerator
// as a map from string to string.
func (pg passwordGenerator) configMap() (map[string]interface{}, error) {
config := make(map[string]interface{})
if err := mapstructure.WeakDecode(pg, &config); err != nil {
return nil, err
}
return config, nil
}
// rsaKeyGenerator generates RSA key pair credentials.
// A zero value rsaKeyGenerator is usable.
type rsaKeyGenerator struct {
// Format is the output format of the generated private key.
// Options include: 'pkcs8' (default)
Format string `mapstructure:"format,omitempty"`
// KeyBits is the bit size of the RSA key to generate.
// Options include: 2048 (default), 3072, and 4096
KeyBits int `mapstructure:"key_bits,omitempty"`
}
// newRSAKeyGenerator returns a new rsaKeyGenerator using the given config.
// Default values will be set on the returned rsaKeyGenerator if not provided
// in the given config.
func newRSAKeyGenerator(config map[string]interface{}) (rsaKeyGenerator, error) {
var kg rsaKeyGenerator
if err := mapstructure.WeakDecode(config, &kg); err != nil {
return kg, err
}
switch strings.ToLower(kg.Format) {
case "":
kg.Format = "pkcs8"
case "pkcs8":
default:
return kg, fmt.Errorf("invalid format: %v", kg.Format)
}
switch kg.KeyBits {
case 0:
kg.KeyBits = 2048
case 2048, 3072, 4096:
default:
return kg, fmt.Errorf("invalid key_bits: %v", kg.KeyBits)
}
return kg, nil
}
// Generate generates an RSA key pair. Returns a PEM-encoded, PKIX marshaled
// public key and a PEM-encoded private key marshaled into the configuration
// format (in that order) or an error.
func (kg *rsaKeyGenerator) generate(r io.Reader) ([]byte, []byte, error) {
reader := rand.Reader
if r != nil {
reader = r
}
var keyBits int
switch kg.KeyBits {
case 0:
keyBits = 2048
case 2048, 3072, 4096:
keyBits = kg.KeyBits
default:
return nil, nil, fmt.Errorf("invalid key_bits: %v", kg.KeyBits)
}
key, err := rsa.GenerateKey(reader, keyBits)
if err != nil {
return nil, nil, err
}
public, err := x509.MarshalPKIXPublicKey(key.Public())
if err != nil {
return nil, nil, err
}
var private []byte
switch strings.ToLower(kg.Format) {
case "", "pkcs8":
private, err = x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("invalid format: %v", kg.Format)
}
publicBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: public,
}
privateBlock := &pem.Block{
Type: "PRIVATE KEY",
Bytes: private,
}
return pem.EncodeToMemory(publicBlock), pem.EncodeToMemory(privateBlock), nil
}
// configMap returns the configuration of the rsaKeyGenerator
// as a map from string to string.
func (kg rsaKeyGenerator) configMap() (map[string]interface{}, error) {
config := make(map[string]interface{})
if err := mapstructure.WeakDecode(kg, &config); err != nil {
return nil, err
}
return config, nil
}

View File

@ -0,0 +1,543 @@
package database
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"testing"
"github.com/hashicorp/vault/sdk/helper/base62"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func Test_newPasswordGenerator(t *testing.T) {
type args struct {
config map[string]interface{}
}
tests := []struct {
name string
args args
want passwordGenerator
wantErr bool
}{
{
name: "newPasswordGenerator with nil config",
args: args{
config: nil,
},
want: passwordGenerator{
PasswordPolicy: "",
},
},
{
name: "newPasswordGenerator without password_policy",
args: args{
config: map[string]interface{}{},
},
want: passwordGenerator{
PasswordPolicy: "",
},
},
{
name: "newPasswordGenerator with password_policy",
args: args{
config: map[string]interface{}{
"password_policy": "test-policy",
},
},
want: passwordGenerator{
PasswordPolicy: "test-policy",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newPasswordGenerator(tt.args.config)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.Equal(t, tt.want, got)
})
}
}
func Test_newRSAKeyGenerator(t *testing.T) {
type args struct {
config map[string]interface{}
}
tests := []struct {
name string
args args
want rsaKeyGenerator
wantErr bool
}{
{
name: "newRSAKeyGenerator with nil config",
args: args{
config: nil,
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with empty config",
args: args{
config: map[string]interface{}{},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with zero value format",
args: args{
config: map[string]interface{}{
"format": "",
},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with zero value key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "0",
},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with format",
args: args{
config: map[string]interface{}{
"format": "pkcs8",
},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with format case insensitive",
args: args{
config: map[string]interface{}{
"format": "PKCS8",
},
},
want: rsaKeyGenerator{
Format: "PKCS8",
KeyBits: 2048,
},
},
{
name: "newRSAKeyGenerator with 3072 key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "3072",
},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 3072,
},
},
{
name: "newRSAKeyGenerator with 4096 key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "4096",
},
},
want: rsaKeyGenerator{
Format: "pkcs8",
KeyBits: 4096,
},
},
{
name: "newRSAKeyGenerator with invalid key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "4097",
},
},
wantErr: true,
},
{
name: "newRSAKeyGenerator with invalid format",
args: args{
config: map[string]interface{}{
"format": "pkcs1",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newRSAKeyGenerator(tt.args.config)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.Equal(t, tt.want, got)
})
}
}
func Test_passwordGenerator_generate(t *testing.T) {
config := logical.TestBackendConfig()
b := Backend(config)
b.Setup(context.Background(), config)
type args struct {
config map[string]interface{}
mock func() interface{}
passGen logical.PasswordGenerator
}
tests := []struct {
name string
args args
wantRegexp string
wantErr bool
}{
{
name: "wrapper missing v4 and v5 interface",
args: args{
mock: func() interface{} {
return nil
},
},
wantErr: true,
},
{
name: "v4: generate password using GenerateCredentials",
args: args{
mock: func() interface{} {
v4Mock := new(mockLegacyDatabase)
v4Mock.On("GenerateCredentials", mock.Anything).
Return("v4-generated-password", nil).
Times(1)
return v4Mock
},
},
wantRegexp: "^v4-generated-password$",
},
{
name: "v5: generate password without policy",
args: args{
mock: func() interface{} {
return new(mockNewDatabase)
},
},
wantRegexp: "^[a-zA-Z0-9-]{20}$",
},
{
name: "v5: generate password with non-existing policy",
args: args{
config: map[string]interface{}{
"password_policy": "not-created",
},
mock: func() interface{} {
return new(mockNewDatabase)
},
},
wantErr: true,
},
{
name: "v5: generate password with existing policy",
args: args{
config: map[string]interface{}{
"password_policy": "test-policy",
},
mock: func() interface{} {
return new(mockNewDatabase)
},
passGen: func() (string, error) {
return base62.Random(30)
},
},
wantRegexp: "^[a-zA-Z0-9]{30}$",
},
{
name: "v5: generate password with existing policy static",
args: args{
config: map[string]interface{}{
"password_policy": "test-policy",
},
mock: func() interface{} {
return new(mockNewDatabase)
},
passGen: func() (string, error) {
return "policy-generated-password", nil
},
},
wantRegexp: "^policy-generated-password$",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up the version wrapper with a mock database implementation
wrapper := databaseVersionWrapper{}
switch m := tt.args.mock().(type) {
case *mockNewDatabase:
wrapper.v5 = m
case *mockLegacyDatabase:
wrapper.v4 = m
}
// Set the password policy for the test case
config.System.(*logical.StaticSystemView).SetPasswordPolicy(
"test-policy", tt.args.passGen)
// Generate the password
pg, err := newPasswordGenerator(tt.args.config)
got, err := pg.generate(context.Background(), b, wrapper)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.Regexp(t, tt.wantRegexp, got)
// Assert all expected calls took place on the mock
if m, ok := wrapper.v5.(*mockNewDatabase); ok {
m.AssertExpectations(t)
}
if m, ok := wrapper.v4.(*mockLegacyDatabase); ok {
m.AssertExpectations(t)
}
})
}
}
func Test_passwordGenerator_configMap(t *testing.T) {
type args struct {
config map[string]interface{}
}
tests := []struct {
name string
args args
want map[string]interface{}
}{
{
name: "nil config results in empty map",
args: args{
config: nil,
},
want: map[string]interface{}{},
},
{
name: "empty config results in empty map",
args: args{
config: map[string]interface{}{},
},
want: map[string]interface{}{},
},
{
name: "input config is equal to output config",
args: args{
config: map[string]interface{}{
"password_policy": "test-policy",
},
},
want: map[string]interface{}{
"password_policy": "test-policy",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pg, err := newPasswordGenerator(tt.args.config)
assert.NoError(t, err)
cm, err := pg.configMap()
assert.NoError(t, err)
assert.Equal(t, tt.want, cm)
})
}
}
func Test_rsaKeyGenerator_generate(t *testing.T) {
type args struct {
config map[string]interface{}
}
tests := []struct {
name string
args args
}{
{
name: "generate RSA key with nil default config",
args: args{
config: nil,
},
},
{
name: "generate RSA key with empty default config",
args: args{
config: map[string]interface{}{},
},
},
{
name: "generate RSA key with 2048 key_bits and format",
args: args{
config: map[string]interface{}{
"key_bits": "2048",
"format": "pkcs8",
},
},
},
{
name: "generate RSA key with 2048 key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "2048",
},
},
},
{
name: "generate RSA key with 3072 key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "3072",
},
},
},
{
name: "generate RSA key with 4096 key_bits",
args: args{
config: map[string]interface{}{
"key_bits": "4096",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Generate the RSA key pair
kg, err := newRSAKeyGenerator(tt.args.config)
public, private, err := kg.generate(rand.Reader)
assert.NoError(t, err)
assert.NotEmpty(t, public)
assert.NotEmpty(t, private)
// Decode the public and private key PEMs
pubBlock, pubRest := pem.Decode(public)
privBlock, privRest := pem.Decode(private)
assert.NotNil(t, pubBlock)
assert.Empty(t, pubRest)
assert.Equal(t, "PUBLIC KEY", pubBlock.Type)
assert.NotNil(t, privBlock)
assert.Empty(t, privRest)
assert.Equal(t, "PRIVATE KEY", privBlock.Type)
// Assert that we can parse the public key PEM block
pub, err := x509.ParsePKIXPublicKey(pubBlock.Bytes)
assert.NoError(t, err)
assert.NotNil(t, pub)
assert.IsType(t, &rsa.PublicKey{}, pub)
// Assert that we can parse the private key PEM block in
// the configured format
switch kg.Format {
case "pkcs8":
priv, err := x509.ParsePKCS8PrivateKey(privBlock.Bytes)
assert.NoError(t, err)
assert.NotNil(t, priv)
assert.IsType(t, &rsa.PrivateKey{}, priv)
default:
t.Fatal("unknown format")
}
})
}
}
func Test_rsaKeyGenerator_configMap(t *testing.T) {
type args struct {
config map[string]interface{}
}
tests := []struct {
name string
args args
want map[string]interface{}
}{
{
name: "nil config results in defaults",
args: args{
config: nil,
},
want: map[string]interface{}{
"format": "pkcs8",
"key_bits": 2048,
},
},
{
name: "empty config results in defaults",
args: args{
config: map[string]interface{}{},
},
want: map[string]interface{}{
"format": "pkcs8",
"key_bits": 2048,
},
},
{
name: "config with format",
args: args{
config: map[string]interface{}{
"format": "pkcs8",
},
},
want: map[string]interface{}{
"format": "pkcs8",
"key_bits": 2048,
},
},
{
name: "config with key_bits",
args: args{
config: map[string]interface{}{
"key_bits": 4096,
},
},
want: map[string]interface{}{
"format": "pkcs8",
"key_bits": 4096,
},
},
{
name: "config with format and key_bits",
args: args{
config: map[string]interface{}{
"format": "pkcs8",
"key_bits": 3072,
},
},
want: map[string]interface{}{
"format": "pkcs8",
"key_bits": 3072,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kg, err := newRSAKeyGenerator(tt.args.config)
assert.NoError(t, err)
cm, err := kg.configMap()
assert.NoError(t, err)
assert.Equal(t, tt.want, cm)
})
}
}

View File

@ -33,6 +33,22 @@ type DatabaseConfig struct {
PasswordPolicy string `json:"password_policy" structs:"password_policy" mapstructure:"password_policy"` PasswordPolicy string `json:"password_policy" structs:"password_policy" mapstructure:"password_policy"`
} }
func (c *DatabaseConfig) SupportsCredentialType(credentialType v5.CredentialType) bool {
credTypes, ok := c.ConnectionDetails[v5.SupportedCredentialTypesKey].([]interface{})
if !ok {
// Default to supporting CredentialTypePassword for database plugins that
// don't specify supported credential types in the initialization response
return credentialType == v5.CredentialTypePassword
}
for _, ct := range credTypes {
if ct == credentialType.String() {
return true
}
}
return false
}
// pathResetConnection configures a path to reset a plugin. // pathResetConnection configures a path to reset a plugin.
func pathResetConnection(b *databaseBackend) *framework.Path { func pathResetConnection(b *databaseBackend) *framework.Path {
return &framework.Path{ return &framework.Path{
@ -347,7 +363,7 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc {
resp := &logical.Response{} resp := &logical.Response{}
// This is a simple test to check for passwords in the connection_url paramater. If one exists, // This is a simple test to check for passwords in the connection_url parameter. If one exists,
// warn the user to use templated url string // warn the user to use templated url string
if connURLRaw, ok := config.ConnectionDetails["connection_url"]; ok { if connURLRaw, ok := config.ConnectionDetails["connection_url"]; ok {
if connURL, err := url.Parse(connURLRaw.(string)); err == nil { if connURL, err := url.Parse(connURLRaw.(string)); err == nil {

View File

@ -72,6 +72,12 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc {
return nil, fmt.Errorf("%q is not an allowed role", name) return nil, fmt.Errorf("%q is not an allowed role", name)
} }
// If the plugin doesn't support the credential type, return an error
if !dbConfig.SupportsCredentialType(role.CredentialType) {
return logical.ErrorResponse("unsupported credential_type: %q",
role.CredentialType.String()), nil
}
// Get the Database object // Get the Database object
dbi, err := b.GetConnection(ctx, req.Storage, role.DBName) dbi, err := b.GetConnection(ctx, req.Storage, role.DBName)
if err != nil { if err != nil {
@ -90,12 +96,6 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc {
// to ensure the database credential does not expire before the lease // to ensure the database credential does not expire before the lease
expiration = expiration.Add(5 * time.Second) expiration = expiration.Add(5 * time.Second)
password, err := dbi.database.GeneratePassword(ctx, b.System(), dbConfig.PasswordPolicy)
if err != nil {
b.CloseIfShutdown(dbi, err)
return nil, fmt.Errorf("unable to generate password: %w", err)
}
newUserReq := v5.NewUserRequest{ newUserReq := v5.NewUserRequest{
UsernameConfig: v5.UsernameMetadata{ UsernameConfig: v5.UsernameMetadata{
DisplayName: req.DisplayName, DisplayName: req.DisplayName,
@ -107,21 +107,70 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc {
RollbackStatements: v5.Statements{ RollbackStatements: v5.Statements{
Commands: role.Statements.Rollback, Commands: role.Statements.Rollback,
}, },
Password: password,
Expiration: expiration, Expiration: expiration,
} }
// Overwriting the password in the event this is a legacy database plugin and the provided password is ignored respData := make(map[string]interface{})
// Generate the credential based on the role's credential type
switch role.CredentialType {
case v5.CredentialTypePassword:
generator, err := newPasswordGenerator(role.CredentialConfig)
if err != nil {
return nil, fmt.Errorf("failed to construct credential generator: %s", err)
}
// Fall back to database config-level password policy if not set on role
if generator.PasswordPolicy == "" {
generator.PasswordPolicy = dbConfig.PasswordPolicy
}
// Generate the password
password, err := generator.generate(ctx, b, dbi.database)
if err != nil {
b.CloseIfShutdown(dbi, err)
return nil, fmt.Errorf("failed to generate password: %s", err)
}
// Set input credential
newUserReq.CredentialType = v5.CredentialTypePassword
newUserReq.Password = password
case v5.CredentialTypeRSAPrivateKey:
generator, err := newRSAKeyGenerator(role.CredentialConfig)
if err != nil {
return nil, fmt.Errorf("failed to construct credential generator: %s", err)
}
// Generate the RSA key pair
public, private, err := generator.generate(b.GetRandomReader())
if err != nil {
return nil, fmt.Errorf("failed to generate RSA key pair: %s", err)
}
// Set input credential
newUserReq.CredentialType = v5.CredentialTypeRSAPrivateKey
newUserReq.PublicKey = public
// Set output credential
respData["rsa_private_key"] = string(private)
}
// Overwriting the password in the event this is a legacy database
// plugin and the provided password is ignored
newUserResp, password, err := dbi.database.NewUser(ctx, newUserReq) newUserResp, password, err := dbi.database.NewUser(ctx, newUserReq)
if err != nil { if err != nil {
b.CloseIfShutdown(dbi, err) b.CloseIfShutdown(dbi, err)
return nil, err return nil, err
} }
respData["username"] = newUserResp.Username
respData := map[string]interface{}{ // Database plugins using the v4 interface generate and return the password.
"username": newUserResp.Username, // Set the password response to what is returned by the NewUser request.
"password": password, if role.CredentialType == v5.CredentialTypePassword {
respData["password"] = password
} }
internal := map[string]interface{}{ internal := map[string]interface{}{
"username": newUserResp.Username, "username": newUserResp.Username,
"role": name, "role": name,
@ -158,14 +207,22 @@ func (b *databaseBackend) pathStaticCredsRead() framework.OperationFunc {
return nil, fmt.Errorf("%q is not an allowed role", name) return nil, fmt.Errorf("%q is not an allowed role", name)
} }
respData := map[string]interface{}{
"username": role.StaticAccount.Username,
"ttl": role.StaticAccount.CredentialTTL().Seconds(),
"rotation_period": role.StaticAccount.RotationPeriod.Seconds(),
"last_vault_rotation": role.StaticAccount.LastVaultRotation,
}
switch role.CredentialType {
case v5.CredentialTypePassword:
respData["password"] = role.StaticAccount.Password
case v5.CredentialTypeRSAPrivateKey:
respData["rsa_private_key"] = string(role.StaticAccount.PrivateKey)
}
return &logical.Response{ return &logical.Response{
Data: map[string]interface{}{ Data: respData,
"username": role.StaticAccount.Username,
"password": role.StaticAccount.Password,
"ttl": role.StaticAccount.PasswordTTL().Seconds(),
"rotation_period": role.StaticAccount.RotationPeriod.Seconds(),
"last_vault_rotation": role.StaticAccount.LastVaultRotation,
},
}, nil }, nil
} }
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/go-secure-stdlib/strutil"
v4 "github.com/hashicorp/vault/sdk/database/dbplugin" v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/locksutil" "github.com/hashicorp/vault/sdk/helper/locksutil"
"github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/logical"
@ -88,6 +89,16 @@ func fieldsForType(roleType string) map[string]*framework.FieldSchema {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the database this role acts on.", Description: "Name of the database this role acts on.",
}, },
"credential_type": {
Type: framework.TypeString,
Description: "The type of credential to manage. Options include: " +
"'password', 'rsa_private_key'. Defaults to 'password'.",
Default: "password",
},
"credential_config": {
Type: framework.TypeKVPairs,
Description: "The configuration for the given credential_type.",
},
} }
// Get the fields that are specific to the type of role, and add them to the // Get the fields that are specific to the type of role, and add them to the
@ -252,6 +263,7 @@ func (b *databaseBackend) pathStaticRoleRead(ctx context.Context, req *logical.R
data := map[string]interface{}{ data := map[string]interface{}{
"db_name": role.DBName, "db_name": role.DBName,
"rotation_statements": role.Statements.Rotation, "rotation_statements": role.Statements.Rotation,
"credential_type": role.CredentialType.String(),
} }
// guard against nil StaticAccount; shouldn't happen but we'll be safe // guard against nil StaticAccount; shouldn't happen but we'll be safe
@ -264,6 +276,9 @@ func (b *databaseBackend) pathStaticRoleRead(ctx context.Context, req *logical.R
} }
} }
if len(role.CredentialConfig) > 0 {
data["credential_config"] = role.CredentialConfig
}
if len(role.Statements.Rotation) == 0 { if len(role.Statements.Rotation) == 0 {
data["rotation_statements"] = []string{} data["rotation_statements"] = []string{}
} }
@ -290,6 +305,10 @@ func (b *databaseBackend) pathRoleRead(ctx context.Context, req *logical.Request
"renew_statements": role.Statements.Renewal, "renew_statements": role.Statements.Renewal,
"default_ttl": role.DefaultTTL.Seconds(), "default_ttl": role.DefaultTTL.Seconds(),
"max_ttl": role.MaxTTL.Seconds(), "max_ttl": role.MaxTTL.Seconds(),
"credential_type": role.CredentialType.String(),
}
if len(role.CredentialConfig) > 0 {
data["credential_config"] = role.CredentialConfig
} }
if len(role.Statements.Creation) == 0 { if len(role.Statements.Creation) == 0 {
data["creation_statements"] = []string{} data["creation_statements"] = []string{}
@ -356,6 +375,23 @@ func (b *databaseBackend) pathRoleCreateUpdate(ctx context.Context, req *logical
if role.DBName == "" { if role.DBName == "" {
return logical.ErrorResponse("database name is required"), nil return logical.ErrorResponse("database name is required"), nil
} }
if credentialTypeRaw, ok := data.GetOk("credential_type"); ok {
credentialType := credentialTypeRaw.(string)
if err := role.setCredentialType(credentialType); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
}
var credentialConfig map[string]string
if raw, ok := data.GetOk("credential_config"); ok {
credentialConfig = raw.(map[string]string)
} else if req.Operation == logical.CreateOperation {
credentialConfig = data.Get("credential_config").(map[string]string)
}
if err := role.setCredentialConfig(credentialConfig); err != nil {
return logical.ErrorResponse("credential_config validation failed: %s", err), nil
}
} }
// Statements // Statements
@ -447,7 +483,7 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
// createRole is a boolean to indicate if this is a new role creation. This is // createRole is a boolean to indicate if this is a new role creation. This is
// can be used later by database plugins that distinguish between creating and // can be used later by database plugins that distinguish between creating and
// updating roles, and may use seperate statements depending on the context. // updating roles, and may use separate statements depending on the context.
createRole := (req.Operation == logical.CreateOperation) createRole := (req.Operation == logical.CreateOperation)
if role == nil { if role == nil {
role = &roleEntry{ role = &roleEntry{
@ -499,6 +535,23 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
role.Statements.Rotation = data.Get("rotation_statements").([]string) role.Statements.Rotation = data.Get("rotation_statements").([]string)
} }
if credentialTypeRaw, ok := data.GetOk("credential_type"); ok {
credentialType := credentialTypeRaw.(string)
if err := role.setCredentialType(credentialType); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
}
var credentialConfig map[string]string
if raw, ok := data.GetOk("credential_config"); ok {
credentialConfig = raw.(map[string]string)
} else if req.Operation == logical.CreateOperation {
credentialConfig = data.Get("credential_config").(map[string]string)
}
if err := role.setCredentialConfig(credentialConfig); err != nil {
return logical.ErrorResponse("credential_config validation failed: %s", err), nil
}
// lvr represents the roles' LastVaultRotation // lvr represents the roles' LastVaultRotation
lvr := role.StaticAccount.LastVaultRotation lvr := role.StaticAccount.LastVaultRotation
@ -558,22 +611,85 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
} }
type roleEntry struct { type roleEntry struct {
DBName string `json:"db_name"` DBName string `json:"db_name"`
Statements v4.Statements `json:"statements"` Statements v4.Statements `json:"statements"`
DefaultTTL time.Duration `json:"default_ttl"` DefaultTTL time.Duration `json:"default_ttl"`
MaxTTL time.Duration `json:"max_ttl"` MaxTTL time.Duration `json:"max_ttl"`
StaticAccount *staticAccount `json:"static_account" mapstructure:"static_account"` CredentialType v5.CredentialType `json:"credential_type"`
CredentialConfig map[string]interface{} `json:"credential_config"`
StaticAccount *staticAccount `json:"static_account" mapstructure:"static_account"`
}
// setCredentialType sets the credential type for the role given its string form.
// Returns an error if the given credential type string is unknown.
func (r *roleEntry) setCredentialType(credentialType string) error {
switch credentialType {
case v5.CredentialTypePassword.String():
r.CredentialType = v5.CredentialTypePassword
case v5.CredentialTypeRSAPrivateKey.String():
r.CredentialType = v5.CredentialTypeRSAPrivateKey
default:
return fmt.Errorf("invalid credential_type %q", credentialType)
}
return nil
}
// setCredentialConfig validates and sets the credential configuration
// for the role using the role's credential type. It will also populate
// all default values. Returns an error if the configuration is invalid.
func (r *roleEntry) setCredentialConfig(config map[string]string) error {
c := make(map[string]interface{})
for k, v := range config {
c[k] = v
}
switch r.CredentialType {
case v5.CredentialTypePassword:
generator, err := newPasswordGenerator(c)
if err != nil {
return err
}
cm, err := generator.configMap()
if err != nil {
return err
}
if len(cm) > 0 {
r.CredentialConfig = cm
}
case v5.CredentialTypeRSAPrivateKey:
generator, err := newRSAKeyGenerator(c)
if err != nil {
return err
}
cm, err := generator.configMap()
if err != nil {
return err
}
if len(cm) > 0 {
r.CredentialConfig = cm
}
}
return nil
} }
type staticAccount struct { type staticAccount struct {
// Username to create or assume management for static accounts // Username to create or assume management for static accounts
Username string `json:"username"` Username string `json:"username"`
// Password is the current password for static accounts. As an input, this is // Password is the current password credential for static accounts. As an input,
// used/required when trying to assume management of an existing static // this is used/required when trying to assume management of an existing static
// account. Return this on credential request if it exists. // account. Returned on credential request if the role's credential type is
// CredentialTypePassword.
Password string `json:"password"` Password string `json:"password"`
// PrivateKey is the current private key 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
// CredentialTypeRSAPrivateKey.
PrivateKey []byte `json:"private_key"`
// LastVaultRotation represents the last time Vault rotated the password // LastVaultRotation represents the last time Vault rotated the password
LastVaultRotation time.Time `json:"last_vault_rotation"` LastVaultRotation time.Time `json:"last_vault_rotation"`
@ -593,7 +709,7 @@ func (s *staticAccount) NextRotationTime() time.Time {
return s.LastVaultRotation.Add(s.RotationPeriod) return s.LastVaultRotation.Add(s.RotationPeriod)
} }
// PasswordTTL calculates the approximate time remaining until the password is // CredentialTTL calculates the approximate time remaining until the credential is
// no longer valid. This is approximate because the periodic rotation is only // no longer valid. This is approximate because the periodic rotation is only
// checked approximately every 5 seconds, and each rotation can take a small // checked approximately every 5 seconds, and each rotation can take a small
// amount of time to process. This can result in a negative TTL time while the // amount of time to process. This can result in a negative TTL time while the
@ -601,7 +717,7 @@ func (s *staticAccount) NextRotationTime() time.Time {
// TTL is negative, zero is returned. Users should not trust passwords with a // TTL is negative, zero is returned. Users should not trust passwords with a
// Zero TTL, as they are likely in the process of being rotated and will quickly // Zero TTL, as they are likely in the process of being rotated and will quickly
// be invalidated. // be invalidated.
func (s *staticAccount) PasswordTTL() time.Duration { func (s *staticAccount) CredentialTTL() time.Duration {
next := s.NextRotationTime() next := s.NextRotationTime()
ttl := next.Sub(time.Now()).Round(time.Second) ttl := next.Sub(time.Now()).Round(time.Second)
if ttl < 0 { if ttl < 0 {
@ -662,7 +778,7 @@ rollback a change if needed.
const pathStaticRoleHelpDesc = ` const pathStaticRoleHelpDesc = `
This path lets you manage the static roles that can be created with this This path lets you manage the static roles that can be created with this
backend. Static Roles are associated with a single database user, and manage the backend. Static Roles are associated with a single database user, and manage the
password based on a rotation period, automatically rotating the password. credential based on a rotation period, automatically rotating the credential.
The "db_name" parameter is required and configures the name of the database The "db_name" parameter is required and configures the name of the database
connection to use. connection to use.
@ -675,7 +791,11 @@ and "}}" to be replaced.
* "name" - The random username generated for the DB user. * "name" - The random username generated for the DB user.
* "password" - The random password generated for the DB user. * "password" - The random password generated for the DB user. Populated if the
static role's credential_type is 'password'.
* "public_key" - The public key generated for the DB user. Populated if the
static role's credential_type is 'rsa_private_key'.
Example of a decent creation_statements for a postgresql database plugin: Example of a decent creation_statements for a postgresql database plugin:

View File

@ -2,6 +2,7 @@ package database
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"strings" "strings"
"testing" "testing"
@ -12,10 +13,193 @@ import (
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql" postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5" v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
var dataKeys = []string{"username", "password", "last_vault_rotation", "rotation_period"} func TestBackend_Roles_CredentialTypes(t *testing.T) {
config := logical.TestBackendConfig()
config.System = logical.TestSystemView()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}
type args struct {
credentialType v5.CredentialType
credentialConfig map[string]string
}
tests := []struct {
name string
args args
wantErr bool
expectedResp map[string]interface{}
}{
{
name: "role with invalid credential type",
args: args{
credentialType: v5.CredentialType(10),
},
wantErr: true,
},
{
name: "role with invalid credential type and valid credential config",
args: args{
credentialType: v5.CredentialType(7),
credentialConfig: map[string]string{
"password_policy": "test-policy",
},
},
wantErr: true,
},
{
name: "role with password credential type",
args: args{
credentialType: v5.CredentialTypePassword,
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypePassword.String(),
"credential_config": nil,
},
},
{
name: "role with password credential type and configuration",
args: args{
credentialType: v5.CredentialTypePassword,
credentialConfig: map[string]string{
"password_policy": "test-policy",
},
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypePassword.String(),
"credential_config": map[string]interface{}{
"password_policy": "test-policy",
},
},
},
{
name: "role with rsa_private_key credential type and default configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypeRSAPrivateKey.String(),
"credential_config": map[string]interface{}{
"key_bits": json.Number("2048"),
"format": "pkcs8",
},
},
},
{
name: "role with rsa_private_key credential type and 2048 bit configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
credentialConfig: map[string]string{
"key_bits": "2048",
},
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypeRSAPrivateKey.String(),
"credential_config": map[string]interface{}{
"key_bits": json.Number("2048"),
"format": "pkcs8",
},
},
},
{
name: "role with rsa_private_key credential type and 3072 bit configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
credentialConfig: map[string]string{
"key_bits": "3072",
},
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypeRSAPrivateKey.String(),
"credential_config": map[string]interface{}{
"key_bits": json.Number("3072"),
"format": "pkcs8",
},
},
},
{
name: "role with rsa_private_key credential type and 4096 bit configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
credentialConfig: map[string]string{
"key_bits": "4096",
},
},
expectedResp: map[string]interface{}{
"credential_type": v5.CredentialTypeRSAPrivateKey.String(),
"credential_config": map[string]interface{}{
"key_bits": json.Number("4096"),
"format": "pkcs8",
},
},
},
{
name: "role with rsa_private_key credential type invalid key_bits configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
credentialConfig: map[string]string{
"key_bits": "256",
},
},
wantErr: true,
},
{
name: "role with rsa_private_key credential type invalid format configuration",
args: args{
credentialType: v5.CredentialTypeRSAPrivateKey,
credentialConfig: map[string]string{
"format": "pkcs1",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := &logical.Request{
Operation: logical.CreateOperation,
Path: "roles/test",
Storage: config.StorageView,
Data: map[string]interface{}{
"db_name": "test-database",
"creation_statements": "CREATE USER {{name}}",
"credential_type": tt.args.credentialType.String(),
"credential_config": tt.args.credentialConfig,
},
}
// Create the role
resp, err := b.HandleRequest(context.Background(), req)
if tt.wantErr {
assert.True(t, resp.IsError(), "expected error")
return
}
assert.False(t, resp.IsError())
assert.Nil(t, err)
// Read the role
req.Operation = logical.ReadOperation
resp, err = b.HandleRequest(context.Background(), req)
assert.False(t, resp.IsError())
assert.Nil(t, err)
for k, v := range tt.expectedResp {
assert.Equal(t, v, resp.Data[k])
}
// Delete the role
req.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), req)
assert.False(t, resp.IsError())
assert.Nil(t, err)
})
}
}
func TestBackend_StaticRole_Config(t *testing.T) { func TestBackend_StaticRole_Config(t *testing.T) {
cluster, sys := getCluster(t) cluster, sys := getCluster(t)
@ -154,6 +338,7 @@ func TestBackend_StaticRole_Config(t *testing.T) {
expected := tc.expected expected := tc.expected
actual := make(map[string]interface{}) actual := make(map[string]interface{})
dataKeys := []string{"username", "password", "last_vault_rotation", "rotation_period"}
for _, key := range dataKeys { for _, key := range dataKeys {
if v, ok := resp.Data[key]; ok { if v, ok := resp.Data[key]; ok {
actual[key] = v actual[key] = v
@ -549,7 +734,6 @@ func TestWALsStillTrackedAfterUpdate(t *testing.T) {
Storage: storage, Storage: storage,
Data: map[string]interface{}{ Data: map[string]interface{}{
"username": "hashicorp", "username": "hashicorp",
"dn": "uid=hashicorp,ou=users,dc=hashicorp,dc=com",
"rotation_period": "600s", "rotation_period": "600s",
}, },
}) })

View File

@ -30,8 +30,8 @@ func pathRotateRootCredentials(b *databaseBackend) []*framework.Path {
}, },
}, },
HelpSynopsis: pathCredsCreateReadHelpSyn, HelpSynopsis: pathRotateCredentialsUpdateHelpSyn,
HelpDescription: pathCredsCreateReadHelpDesc, HelpDescription: pathRotateCredentialsUpdateHelpDesc,
}, },
{ {
Pattern: "rotate-role/" + framework.GenericNameRegex("name"), Pattern: "rotate-role/" + framework.GenericNameRegex("name"),
@ -50,8 +50,8 @@ func pathRotateRootCredentials(b *databaseBackend) []*framework.Path {
}, },
}, },
HelpSynopsis: pathCredsCreateReadHelpSyn, HelpSynopsis: pathRotateRoleCredentialsUpdateHelpSyn,
HelpDescription: pathCredsCreateReadHelpDesc, HelpDescription: pathRotateRoleCredentialsUpdateHelpDesc,
}, },
} }
} }
@ -96,11 +96,18 @@ func (b *databaseBackend) pathRotateRootCredentialsUpdate() framework.OperationF
delete(b.connections, name) delete(b.connections, name)
}() }()
generator, err := newPasswordGenerator(nil)
if err != nil {
return nil, fmt.Errorf("failed to construct credential generator: %s", err)
}
generator.PasswordPolicy = config.PasswordPolicy
// Generate new credentials // Generate new credentials
oldPassword := config.ConnectionDetails["password"].(string) oldPassword := config.ConnectionDetails["password"].(string)
newPassword, err := dbi.database.GeneratePassword(ctx, b.System(), config.PasswordPolicy) newPassword, err := generator.generate(ctx, b, dbi.database)
if err != nil { if err != nil {
return nil, err b.CloseIfShutdown(dbi, err)
return nil, fmt.Errorf("failed to generate password: %s", err)
} }
config.ConnectionDetails["password"] = newPassword config.ConnectionDetails["password"] = newPassword
@ -116,7 +123,8 @@ func (b *databaseBackend) pathRotateRootCredentialsUpdate() framework.OperationF
} }
updateReq := v5.UpdateUserRequest{ updateReq := v5.UpdateUserRequest{
Username: rootUsername, Username: rootUsername,
CredentialType: v5.CredentialTypePassword,
Password: &v5.ChangePassword{ Password: &v5.ChangePassword{
NewPassword: newPassword, NewPassword: newPassword,
Statements: v5.Statements{ Statements: v5.Statements{

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"github.com/hashicorp/vault/sdk/database/dbplugin" "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5" v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/logical"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -94,7 +93,8 @@ func (b *databaseBackend) rollbackDatabaseCredentials(ctx context.Context, confi
}() }()
updateReq := v5.UpdateUserRequest{ updateReq := v5.UpdateUserRequest{
Username: entry.UserName, Username: entry.UserName,
CredentialType: v5.CredentialTypePassword,
Password: &v5.ChangePassword{ Password: &v5.ChangePassword{
NewPassword: entry.OldPassword, NewPassword: entry.OldPassword,
Statements: v5.Statements{ Statements: v5.Statements{

View File

@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/go-secure-stdlib/strutil"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5" v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/framework"
@ -125,9 +124,12 @@ func (b *databaseBackend) runTicker(ctx context.Context, queueTickInterval time.
// setCredentialsWAL is used to store information in a WAL that can retry a // setCredentialsWAL is used to store information in a WAL that can retry a
// credential setting or rotation in the event of partial failure. // credential setting or rotation in the event of partial failure.
type setCredentialsWAL struct { type setCredentialsWAL struct {
NewPassword string `json:"new_password"` CredentialType v5.CredentialType `json:"credential_type"`
RoleName string `json:"role_name"` NewPassword string `json:"new_password"`
Username string `json:"username"` NewPublicKey []byte `json:"new_public_key"`
NewPrivateKey []byte `json:"new_private_key"`
RoleName string `json:"role_name"`
Username string `json:"username"`
LastVaultRotation time.Time `json:"last_vault_rotation"` LastVaultRotation time.Time `json:"last_vault_rotation"`
@ -136,6 +138,24 @@ type setCredentialsWAL struct {
walCreatedAt int64 // Unix time at which the WAL was created. walCreatedAt int64 // Unix time at which the WAL was created.
} }
// credentialIsSet returns true if the credential associated with the
// CredentialType field is properly set. See field comments to for a
// mapping of CredentialType values to respective credential fields.
func (w *setCredentialsWAL) credentialIsSet() bool {
if w == nil {
return false
}
switch w.CredentialType {
case v5.CredentialTypePassword:
return w.NewPassword != ""
case v5.CredentialTypeRSAPrivateKey:
return len(w.NewPublicKey) > 0
default:
return false
}
}
// rotateCredentials sets a new password for a static account. This method is // rotateCredentials sets a new password for a static account. This method is
// invoked in the runTicker method, which is in it's own go-routine, and invoked // invoked in the runTicker method, which is in it's own go-routine, and invoked
// periodically (approximately every 5 seconds). // periodically (approximately every 5 seconds).
@ -287,16 +307,17 @@ type setStaticAccountOutput struct {
WALID string WALID string
} }
// setStaticAccount sets the password for a static account associated with a // setStaticAccount sets the credential for a static account associated with a
// Role. This method does many things: // Role. This method does many things:
// - verifies role exists and is in the allowed roles list // - verifies role exists and is in the allowed roles list
// - loads an existing WAL entry if WALID input is given, otherwise creates a // - loads an existing WAL entry if WALID input is given, otherwise creates a
// new WAL entry // new WAL entry
// - gets a database connection // - gets a database connection
// - accepts an input password, otherwise generates a new one via gRPC to the // - accepts an input credential, otherwise generates a new one for
// database plugin // the role's credential type
// - sets new password for the static account // - sets new credential for the static account
// - uses WAL for ensuring passwords are not lost if storage to Vault fails // - uses WAL for ensuring new credentials are not lost if storage to Vault fails,
// resulting in a partial failure.
// //
// This method does not perform any operations on the priority queue. Those // This method does not perform any operations on the priority queue. Those
// tasks must be handled outside of this method. // tasks must be handled outside of this method.
@ -321,6 +342,12 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
return output, fmt.Errorf("%q is not an allowed role", input.RoleName) return output, fmt.Errorf("%q is not an allowed role", input.RoleName)
} }
// If the plugin doesn't support the credential type, return an error
if !dbConfig.SupportsCredentialType(input.Role.CredentialType) {
return output, fmt.Errorf("unsupported credential_type: %q",
input.Role.CredentialType.String())
}
// Get the Database object // Get the Database object
dbi, err := b.GetConnection(ctx, s, input.Role.DBName) dbi, err := b.GetConnection(ctx, s, input.Role.DBName)
if err != nil { if err != nil {
@ -330,61 +357,122 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
dbi.RLock() dbi.RLock()
defer dbi.RUnlock() defer dbi.RUnlock()
// Use password from input if available. This happens if we're restoring from updateReq := v5.UpdateUserRequest{
Username: input.Role.StaticAccount.Username,
}
statements := v5.Statements{
Commands: input.Role.Statements.Rotation,
}
// 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 // a WAL item or processing the rotation queue with an item that has a WAL
// associated with it // associated with it
var newPassword string
if output.WALID != "" { if output.WALID != "" {
wal, err := b.findStaticWAL(ctx, s, output.WALID) wal, err := b.findStaticWAL(ctx, s, output.WALID)
if err != nil { if err != nil {
return output, errwrap.Wrapf("error retrieving WAL entry: {{err}}", err) return output, fmt.Errorf("error retrieving WAL entry: %w", err)
} }
switch { switch {
case wal != nil && wal.NewPassword != "": case wal == nil:
newPassword = wal.NewPassword b.Logger().Error("expected role to have WAL, but WAL not found in storage", "role", input.RoleName, "WAL ID", output.WALID)
default:
if wal == nil { // Generate a new WAL entry and credential
b.Logger().Error("expected role to have WAL, but WAL not found in storage", "role", input.RoleName, "WAL ID", output.WALID)
} else {
b.Logger().Error("expected WAL to have a new password set, but empty", "role", input.RoleName, "WAL ID", output.WALID)
err = framework.DeleteWAL(ctx, s, output.WALID)
if err != nil {
b.Logger().Warn("failed to delete WAL with no new password", "error", err, "WAL ID", output.WALID)
}
}
// If there's anything wrong with the WAL in storage, we'll need
// to generate a fresh WAL and password
output.WALID = "" output.WALID = ""
case !wal.credentialIsSet():
b.Logger().Error("expected WAL to have a new credential set, but empty", "role", input.RoleName, "WAL ID", output.WALID)
if err := framework.DeleteWAL(ctx, s, output.WALID); err != nil {
b.Logger().Warn("failed to delete WAL with no new credential", "error", err, "WAL ID", output.WALID)
}
// Generate a new WAL entry and credential
output.WALID = ""
case wal.CredentialType == v5.CredentialTypePassword:
// Roll forward by using the credential in the existing WAL entry
updateReq.CredentialType = v5.CredentialTypePassword
updateReq.Password = &v5.ChangePassword{
NewPassword: wal.NewPassword,
Statements: statements,
}
input.Role.StaticAccount.Password = wal.NewPassword
case wal.CredentialType == v5.CredentialTypeRSAPrivateKey:
// Roll forward by using the credential in the existing WAL entry
updateReq.CredentialType = v5.CredentialTypeRSAPrivateKey
updateReq.PublicKey = &v5.ChangePublicKey{
NewPublicKey: wal.NewPublicKey,
Statements: statements,
}
input.Role.StaticAccount.PrivateKey = wal.NewPrivateKey
} }
} }
// Generate a new credential
if output.WALID == "" { if output.WALID == "" {
newPassword, err = dbi.database.GeneratePassword(ctx, b.System(), dbConfig.PasswordPolicy) walEntry := &setCredentialsWAL{
if err != nil {
return output, err
}
output.WALID, err = framework.PutWAL(ctx, s, staticWALKey, &setCredentialsWAL{
RoleName: input.RoleName, RoleName: input.RoleName,
Username: input.Role.StaticAccount.Username, Username: input.Role.StaticAccount.Username,
NewPassword: newPassword,
LastVaultRotation: input.Role.StaticAccount.LastVaultRotation, LastVaultRotation: input.Role.StaticAccount.LastVaultRotation,
}) }
switch input.Role.CredentialType {
case v5.CredentialTypePassword:
generator, err := newPasswordGenerator(input.Role.CredentialConfig)
if err != nil {
return output, fmt.Errorf("failed to construct credential generator: %s", err)
}
// Fall back to database config-level password policy if not set on role
if generator.PasswordPolicy == "" {
generator.PasswordPolicy = dbConfig.PasswordPolicy
}
// Generate the password
newPassword, err := generator.generate(ctx, b, dbi.database)
if err != nil {
b.CloseIfShutdown(dbi, err)
return output, fmt.Errorf("failed to generate password: %s", err)
}
// Set new credential in WAL entry and update user request
walEntry.NewPassword = newPassword
updateReq.CredentialType = v5.CredentialTypePassword
updateReq.Password = &v5.ChangePassword{
NewPassword: newPassword,
Statements: statements,
}
// Set new credential in static account
input.Role.StaticAccount.Password = newPassword
case v5.CredentialTypeRSAPrivateKey:
generator, err := newRSAKeyGenerator(input.Role.CredentialConfig)
if err != nil {
return output, fmt.Errorf("failed to construct credential generator: %s", err)
}
// Generate the RSA key pair
public, private, err := generator.generate(b.GetRandomReader())
if err != nil {
return output, fmt.Errorf("failed to generate RSA key pair: %s", err)
}
// Set new credential in WAL entry and update user request
walEntry.NewPublicKey = public
updateReq.CredentialType = v5.CredentialTypeRSAPrivateKey
updateReq.PublicKey = &v5.ChangePublicKey{
NewPublicKey: public,
Statements: statements,
}
// Set new credential in static account
input.Role.StaticAccount.PrivateKey = private
}
output.WALID, err = framework.PutWAL(ctx, s, staticWALKey, walEntry)
if err != nil { if err != nil {
return output, fmt.Errorf("error writing WAL entry: %w", err) return output, fmt.Errorf("error writing WAL entry: %w", err)
} }
b.Logger().Debug("writing WAL", "role", input.RoleName, "WAL ID", output.WALID) b.Logger().Debug("writing WAL", "role", input.RoleName, "WAL ID", output.WALID)
} }
updateReq := v5.UpdateUserRequest{
Username: input.Role.StaticAccount.Username,
Password: &v5.ChangePassword{
NewPassword: newPassword,
Statements: v5.Statements{
Commands: input.Role.Statements.Rotation,
},
},
}
_, err = dbi.database.UpdateUser(ctx, updateReq, false) _, err = dbi.database.UpdateUser(ctx, updateReq, false)
if err != nil { if err != nil {
b.CloseIfShutdown(dbi, err) b.CloseIfShutdown(dbi, err)
@ -395,7 +483,6 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
// lvr is the known LastVaultRotation // lvr is the known LastVaultRotation
lvr := time.Now() lvr := time.Now()
input.Role.StaticAccount.LastVaultRotation = lvr input.Role.StaticAccount.LastVaultRotation = lvr
input.Role.StaticAccount.Password = newPassword
output.RotationTime = lvr output.RotationTime = lvr
entry, err := logical.StorageEntryJSON(databaseStaticRolePath+input.RoleName, input.Role) entry, err := logical.StorageEntryJSON(databaseStaticRolePath+input.RoleName, input.Role)

View File

@ -2,12 +2,10 @@ package database
import ( import (
"context" "context"
"crypto/rand"
"fmt" "fmt"
log "github.com/hashicorp/go-hclog" log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/helper/random"
v4 "github.com/hashicorp/vault/sdk/database/dbplugin" v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5" v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/helper/pluginutil"
@ -229,39 +227,6 @@ func (d databaseVersionWrapper) Close() error {
return d.v4.Close() return d.v4.Close()
} }
// /////////////////////////////////////////////////////////////////////////////////
// Password generation
// /////////////////////////////////////////////////////////////////////////////////
type passwordGenerator interface {
GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error)
}
var defaultPasswordGenerator = random.DefaultStringGenerator
// GeneratePassword either from the v4 database or by using the provided password policy. If using a v5 database
// and no password policy is specified, this will have a reasonable default password generator.
func (d databaseVersionWrapper) GeneratePassword(ctx context.Context, generator passwordGenerator, passwordPolicy string) (password string, err error) {
if !d.isV5() && !d.isV4() {
return "", fmt.Errorf("no underlying database specified")
}
// If using the legacy database, use GenerateCredentials instead of password policies
// This will keep the existing behavior even though passwords can be generated with a policy
if d.isV4() {
password, err := d.v4.GenerateCredentials(ctx)
if err != nil {
return "", err
}
return password, nil
}
if passwordPolicy == "" {
return defaultPasswordGenerator.Generate(ctx, rand.Reader)
}
return generator.GeneratePasswordFromPolicy(ctx, passwordPolicy)
}
func (d databaseVersionWrapper) isV5() bool { func (d databaseVersionWrapper) isV5() bool {
return d.v5 != nil return d.v5 != nil
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"testing" "testing"
"time" "time"
@ -176,176 +175,6 @@ func TestInitDatabase_legacyDB(t *testing.T) {
} }
} }
type fakePasswordGenerator struct {
password string
err error
}
func (pg fakePasswordGenerator) GeneratePasswordFromPolicy(ctx context.Context, policy string) (string, error) {
return pg.password, pg.err
}
func TestGeneratePassword_missingDB(t *testing.T) {
dbw := databaseVersionWrapper{}
gen := fakePasswordGenerator{
err: fmt.Errorf("this shouldn't be called"),
}
pass, err := dbw.GeneratePassword(context.Background(), gen, "policy")
if err == nil {
t.Fatalf("err expected, got nil")
}
if pass != "" {
t.Fatalf("Password should be empty but was: %s", pass)
}
}
func TestGeneratePassword_legacy(t *testing.T) {
type testCase struct {
legacyPassword string
legacyErr error
legacyCalls int
expectedPassword string
expectErr bool
}
tests := map[string]testCase{
"legacy password generation": {
legacyPassword: "legacy_password",
legacyErr: nil,
legacyCalls: 1,
expectedPassword: "legacy_password",
expectErr: false,
},
"legacy password failure": {
legacyPassword: "",
legacyErr: fmt.Errorf("failed :("),
legacyCalls: 1,
expectedPassword: "",
expectErr: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
legacyDB := new(mockLegacyDatabase)
legacyDB.On("GenerateCredentials", mock.Anything).
Return(test.legacyPassword, test.legacyErr)
defer legacyDB.AssertNumberOfCalls(t, "GenerateCredentials", test.legacyCalls)
dbw := databaseVersionWrapper{
v4: legacyDB,
}
passGen := fakePasswordGenerator{
err: fmt.Errorf("this should not be called"),
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
password, err := dbw.GeneratePassword(ctx, passGen, "test_policy")
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if password != test.expectedPassword {
t.Fatalf("Actual password: %s Expected password: %s", password, test.expectedPassword)
}
})
}
}
func TestGeneratePassword_policies(t *testing.T) {
type testCase struct {
passwordPolicyPassword string
passwordPolicyErr error
expectedPassword string
expectErr bool
}
tests := map[string]testCase{
"password policy generation": {
passwordPolicyPassword: "new_password",
expectedPassword: "new_password",
expectErr: false,
},
"password policy error": {
passwordPolicyPassword: "",
passwordPolicyErr: fmt.Errorf("test error"),
expectedPassword: "",
expectErr: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
newDB := new(mockNewDatabase)
defer newDB.AssertExpectations(t)
dbw := databaseVersionWrapper{
v5: newDB,
}
passGen := fakePasswordGenerator{
password: test.passwordPolicyPassword,
err: test.passwordPolicyErr,
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
password, err := dbw.GeneratePassword(ctx, passGen, "test_policy")
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if password != test.expectedPassword {
t.Fatalf("Actual password: %s Expected password: %s", password, test.expectedPassword)
}
})
}
}
func TestGeneratePassword_no_policy(t *testing.T) {
newDB := new(mockNewDatabase)
defer newDB.AssertExpectations(t)
dbw := databaseVersionWrapper{
v5: newDB,
}
passGen := fakePasswordGenerator{
password: "",
err: fmt.Errorf("should not be called"),
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
password, err := dbw.GeneratePassword(ctx, passGen, "")
if err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if password == "" {
t.Fatalf("missing password")
}
rawRegex := "^[a-zA-Z0-9-]{20}$"
re := regexp.MustCompile(rawRegex)
if !re.MatchString(password) {
t.Fatalf("password %q did not match regex: %q", password, rawRegex)
}
}
func TestNewUser_missingDB(t *testing.T) { func TestNewUser_missingDB(t *testing.T) {
dbw := databaseVersionWrapper{} dbw := databaseVersionWrapper{}

3
changelog/15376.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
**Snowflake Database Plugin**: Adds ability to manage RSA key pair credentials for dynamic and static Snowflake users.
```

2
go.mod
View File

@ -118,7 +118,7 @@ require (
github.com/hashicorp/vault/api v1.5.0 github.com/hashicorp/vault/api v1.5.0
github.com/hashicorp/vault/api/auth/approle v0.1.0 github.com/hashicorp/vault/api/auth/approle v0.1.0
github.com/hashicorp/vault/api/auth/userpass v0.1.0 github.com/hashicorp/vault/api/auth/userpass v0.1.0
github.com/hashicorp/vault/sdk v0.4.2-0.20220426194706-f8e907e0deda github.com/hashicorp/vault/sdk v0.4.2-0.20220511000023-fa93782110bf
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab
github.com/jcmturner/gokrb5/v8 v8.4.2 github.com/jcmturner/gokrb5/v8 v8.4.2
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f

View File

@ -9,7 +9,6 @@ import (
"unicode" "unicode"
"github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto" "github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
) )
@ -60,8 +59,10 @@ func TestConversionsHaveAllFields(t *testing.T) {
"rollback_statement", "rollback_statement",
}, },
}, },
Password: "password", CredentialType: CredentialTypeRSAPrivateKey,
Expiration: time.Now(), PublicKey: []byte("-----BEGIN PUBLIC KEY-----"),
Password: "password",
Expiration: time.Now(),
} }
protoReq, err := newUserReqToProto(req) protoReq, err := newUserReqToProto(req)
@ -85,7 +86,8 @@ func TestConversionsHaveAllFields(t *testing.T) {
t.Run("updateUserReqToProto", func(t *testing.T) { t.Run("updateUserReqToProto", func(t *testing.T) {
req := UpdateUserRequest{ req := UpdateUserRequest{
Username: "username", Username: "username",
CredentialType: CredentialTypeRSAPrivateKey,
Password: &ChangePassword{ Password: &ChangePassword{
NewPassword: "newpassword", NewPassword: "newpassword",
Statements: Statements{ Statements: Statements{
@ -94,6 +96,14 @@ func TestConversionsHaveAllFields(t *testing.T) {
}, },
}, },
}, },
PublicKey: &ChangePublicKey{
NewPublicKey: []byte("-----BEGIN PUBLIC KEY-----"),
Statements: Statements{
Commands: []string{
"statement",
},
},
},
Expiration: &ChangeExpiration{ Expiration: &ChangeExpiration{
NewExpiration: time.Now(), NewExpiration: time.Now(),
Statements: Statements{ Statements: Statements{
@ -154,7 +164,8 @@ func TestConversionsHaveAllFields(t *testing.T) {
t.Run("getUpdateUserRequest", func(t *testing.T) { t.Run("getUpdateUserRequest", func(t *testing.T) {
req := &proto.UpdateUserRequest{ req := &proto.UpdateUserRequest{
Username: "username", Username: "username",
CredentialType: int32(CredentialTypeRSAPrivateKey),
Password: &proto.ChangePassword{ Password: &proto.ChangePassword{
NewPassword: "newpass", NewPassword: "newpass",
Statements: &proto.Statements{ Statements: &proto.Statements{
@ -163,6 +174,14 @@ func TestConversionsHaveAllFields(t *testing.T) {
}, },
}, },
}, },
PublicKey: &proto.ChangePublicKey{
NewPublicKey: []byte("-----BEGIN PUBLIC KEY-----"),
Statements: &proto.Statements{
Commands: []string{
"statement",
},
},
},
Expiration: &proto.ChangeExpiration{ Expiration: &proto.ChangeExpiration{
NewExpiration: timestamppb.Now(), NewExpiration: timestamppb.Now(),
Statements: &proto.Statements{ Statements: &proto.Statements{

View File

@ -71,6 +71,22 @@ type InitializeResponse struct {
Config map[string]interface{} Config map[string]interface{}
} }
// SupportedCredentialTypesKey is used to get and set the supported
// CredentialType values in database plugins and Vault.
const SupportedCredentialTypesKey = "supported_credential_types"
// SetSupportedCredentialTypes sets the CredentialType values that are
// supported by the database plugin. It can be used by database plugins
// to communicate what CredentialType values it supports managing.
func (ir InitializeResponse) SetSupportedCredentialTypes(credTypes []CredentialType) {
sct := make([]string, 0, len(credTypes))
for _, t := range credTypes {
sct = append(sct, t.String())
}
ir.Config[SupportedCredentialTypesKey] = sct
}
// /////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////
// NewUser() // NewUser()
// /////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////
@ -90,9 +106,20 @@ type NewUserRequest struct {
// if the new user creation process fails. // if the new user creation process fails.
RollbackStatements Statements RollbackStatements Statements
// Password credentials to use when creating the user // CredentialType is the type of credential to use when creating a user.
// Respective fields for the credential type will contain the credential
// value that was generated by Vault.
CredentialType CredentialType
// Password credential to use when creating the user.
// Value is set when the credential type is CredentialTypePassword.
Password string Password string
// PublicKey credential to use when creating the user.
// The value is a PKIX marshaled, PEM encoded public key.
// The value is set when the credential type is CredentialTypeRSAPrivateKey.
PublicKey []byte
// Expiration of the user. Not all database plugins will support this. // Expiration of the user. Not all database plugins will support this.
Expiration time.Time Expiration time.Time
} }
@ -110,6 +137,25 @@ type NewUserResponse struct {
Username string Username string
} }
// CredentialType is a type of database credential.
type CredentialType int
const (
CredentialTypePassword CredentialType = iota
CredentialTypeRSAPrivateKey
)
func (k CredentialType) String() string {
switch k {
case CredentialTypePassword:
return "password"
case CredentialTypeRSAPrivateKey:
return "rsa_private_key"
default:
return "unknown"
}
}
// /////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////
// UpdateUser() // UpdateUser()
// /////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////
@ -118,15 +164,37 @@ type UpdateUserRequest struct {
// Username to make changes to. // Username to make changes to.
Username string Username string
// CredentialType is the type of credential to use when updating a user.
// Respective fields for the credential type will contain the credential
// value that was generated by Vault.
CredentialType CredentialType
// Password indicates the new password to change to. // Password indicates the new password to change to.
// The value is set when the credential type is CredentialTypePassword.
// If nil, no change is requested. // If nil, no change is requested.
Password *ChangePassword Password *ChangePassword
// PublicKey indicates the new public key to change to.
// The value is set when the credential type is CredentialTypeRSAPrivateKey.
// If nil, no change is requested.
PublicKey *ChangePublicKey
// Expiration indicates the new expiration date to change to. // Expiration indicates the new expiration date to change to.
// If nil, no change is requested. // If nil, no change is requested.
Expiration *ChangeExpiration Expiration *ChangeExpiration
} }
// ChangePublicKey of a given user
type ChangePublicKey struct {
// NewPublicKey is the new public key credential for the user.
// The value is a PKIX marshaled, PEM encoded public key.
NewPublicKey []byte
// Statements is an ordered list of commands to run within the database
// when changing the user's public key credential.
Statements Statements
}
// ChangePassword of a given user // ChangePassword of a given user
type ChangePassword struct { type ChangePassword struct {
// NewPassword for the user // NewPassword for the user

View File

@ -95,8 +95,10 @@ func newUserReqToProto(req NewUserRequest) (*proto.NewUserRequest, error) {
DisplayName: req.UsernameConfig.DisplayName, DisplayName: req.UsernameConfig.DisplayName,
RoleName: req.UsernameConfig.RoleName, RoleName: req.UsernameConfig.RoleName,
}, },
Password: req.Password, CredentialType: int32(req.CredentialType),
Expiration: expiration, Password: req.Password,
PublicKey: req.PublicKey,
Expiration: expiration,
Statements: &proto.Statements{ Statements: &proto.Statements{
Commands: req.Statements.Commands, Commands: req.Statements.Commands,
}, },
@ -138,6 +140,7 @@ func updateUserReqToProto(req UpdateUserRequest) (*proto.UpdateUserRequest, erro
} }
if (req.Password == nil || req.Password.NewPassword == "") && if (req.Password == nil || req.Password.NewPassword == "") &&
(req.PublicKey == nil || len(req.PublicKey.NewPublicKey) == 0) &&
(req.Expiration == nil || req.Expiration.NewExpiration.IsZero()) { (req.Expiration == nil || req.Expiration.NewExpiration.IsZero()) {
return nil, fmt.Errorf("missing changes") return nil, fmt.Errorf("missing changes")
} }
@ -157,10 +160,22 @@ func updateUserReqToProto(req UpdateUserRequest) (*proto.UpdateUserRequest, erro
} }
} }
var publicKey *proto.ChangePublicKey
if req.PublicKey != nil && len(req.PublicKey.NewPublicKey) > 0 {
publicKey = &proto.ChangePublicKey{
NewPublicKey: req.PublicKey.NewPublicKey,
Statements: &proto.Statements{
Commands: req.PublicKey.Statements.Commands,
},
}
}
rpcReq := &proto.UpdateUserRequest{ rpcReq := &proto.UpdateUserRequest{
Username: req.Username, Username: req.Username,
Password: password, CredentialType: int32(req.CredentialType),
Expiration: expiration, Password: password,
PublicKey: publicKey,
Expiration: expiration,
} }
return rpcReq, nil return rpcReq, nil
} }

View File

@ -159,7 +159,9 @@ func (g *gRPCServer) NewUser(ctx context.Context, req *proto.NewUserRequest) (*p
DisplayName: req.GetUsernameConfig().GetDisplayName(), DisplayName: req.GetUsernameConfig().GetDisplayName(),
RoleName: req.GetUsernameConfig().GetRoleName(), RoleName: req.GetUsernameConfig().GetRoleName(),
}, },
CredentialType: CredentialType(req.GetCredentialType()),
Password: req.GetPassword(), Password: req.GetPassword(),
PublicKey: req.GetPublicKey(),
Expiration: expiration, Expiration: expiration,
Statements: getStatementsFromProto(req.GetStatements()), Statements: getStatementsFromProto(req.GetStatements()),
RollbackStatements: getStatementsFromProto(req.GetRollbackStatements()), RollbackStatements: getStatementsFromProto(req.GetRollbackStatements()),
@ -207,6 +209,14 @@ func getUpdateUserRequest(req *proto.UpdateUserRequest) (UpdateUserRequest, erro
} }
} }
var publicKey *ChangePublicKey
if req.GetPublicKey() != nil && len(req.GetPublicKey().GetNewPublicKey()) > 0 {
publicKey = &ChangePublicKey{
NewPublicKey: req.GetPublicKey().GetNewPublicKey(),
Statements: getStatementsFromProto(req.GetPublicKey().GetStatements()),
}
}
var expiration *ChangeExpiration var expiration *ChangeExpiration
if req.GetExpiration() != nil && req.GetExpiration().GetNewExpiration() != nil { if req.GetExpiration() != nil && req.GetExpiration().GetNewExpiration() != nil {
newExpiration, err := ptypes.Timestamp(req.GetExpiration().GetNewExpiration()) newExpiration, err := ptypes.Timestamp(req.GetExpiration().GetNewExpiration())
@ -221,9 +231,11 @@ func getUpdateUserRequest(req *proto.UpdateUserRequest) (UpdateUserRequest, erro
} }
dbReq := UpdateUserRequest{ dbReq := UpdateUserRequest{
Username: req.GetUsername(), Username: req.GetUsername(),
Password: password, CredentialType: CredentialType(req.GetCredentialType()),
Expiration: expiration, Password: password,
PublicKey: publicKey,
Expiration: expiration,
} }
if !hasChange(dbReq) { if !hasChange(dbReq) {
@ -237,6 +249,9 @@ func hasChange(dbReq UpdateUserRequest) bool {
if dbReq.Password != nil && dbReq.Password.NewPassword != "" { if dbReq.Password != nil && dbReq.Password.NewPassword != "" {
return true return true
} }
if dbReq.PublicKey != nil && len(dbReq.PublicKey.NewPublicKey) > 0 {
return true
}
if dbReq.Expiration != nil && !dbReq.Expiration.NewExpiration.IsZero() { if dbReq.Expiration != nil && !dbReq.Expiration.NewExpiration.IsZero() {
return true return true
} }

View File

@ -137,6 +137,8 @@ type NewUserRequest struct {
Expiration *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` Expiration *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"`
Statements *Statements `protobuf:"bytes,4,opt,name=statements,proto3" json:"statements,omitempty"` Statements *Statements `protobuf:"bytes,4,opt,name=statements,proto3" json:"statements,omitempty"`
RollbackStatements *Statements `protobuf:"bytes,5,opt,name=rollback_statements,json=rollbackStatements,proto3" json:"rollback_statements,omitempty"` RollbackStatements *Statements `protobuf:"bytes,5,opt,name=rollback_statements,json=rollbackStatements,proto3" json:"rollback_statements,omitempty"`
CredentialType int32 `protobuf:"varint,6,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty"`
PublicKey []byte `protobuf:"bytes,7,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
} }
func (x *NewUserRequest) Reset() { func (x *NewUserRequest) Reset() {
@ -206,6 +208,20 @@ func (x *NewUserRequest) GetRollbackStatements() *Statements {
return nil return nil
} }
func (x *NewUserRequest) GetCredentialType() int32 {
if x != nil {
return x.CredentialType
}
return 0
}
func (x *NewUserRequest) GetPublicKey() []byte {
if x != nil {
return x.PublicKey
}
return nil
}
type UsernameConfig struct { type UsernameConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -316,9 +332,11 @@ type UpdateUserRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,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"` Password *ChangePassword `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Expiration *ChangeExpiration `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,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"`
} }
func (x *UpdateUserRequest) Reset() { func (x *UpdateUserRequest) Reset() {
@ -374,6 +392,20 @@ func (x *UpdateUserRequest) GetExpiration() *ChangeExpiration {
return nil return nil
} }
func (x *UpdateUserRequest) GetPublicKey() *ChangePublicKey {
if x != nil {
return x.PublicKey
}
return nil
}
func (x *UpdateUserRequest) GetCredentialType() int32 {
if x != nil {
return x.CredentialType
}
return 0
}
type ChangePassword struct { type ChangePassword struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -429,6 +461,61 @@ func (x *ChangePassword) GetStatements() *Statements {
return nil return nil
} }
type ChangePublicKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NewPublicKey []byte `protobuf:"bytes,1,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty"`
Statements *Statements `protobuf:"bytes,2,opt,name=statements,proto3" json:"statements,omitempty"`
}
func (x *ChangePublicKey) Reset() {
*x = ChangePublicKey{}
if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChangePublicKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChangePublicKey) ProtoMessage() {}
func (x *ChangePublicKey) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7]
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 ChangePublicKey.ProtoReflect.Descriptor instead.
func (*ChangePublicKey) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{7}
}
func (x *ChangePublicKey) GetNewPublicKey() []byte {
if x != nil {
return x.NewPublicKey
}
return nil
}
func (x *ChangePublicKey) GetStatements() *Statements {
if x != nil {
return x.Statements
}
return nil
}
type ChangeExpiration struct { type ChangeExpiration struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -441,7 +528,7 @@ type ChangeExpiration struct {
func (x *ChangeExpiration) Reset() { func (x *ChangeExpiration) Reset() {
*x = ChangeExpiration{} *x = ChangeExpiration{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -454,7 +541,7 @@ func (x *ChangeExpiration) String() string {
func (*ChangeExpiration) ProtoMessage() {} func (*ChangeExpiration) ProtoMessage() {}
func (x *ChangeExpiration) ProtoReflect() protoreflect.Message { func (x *ChangeExpiration) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -467,7 +554,7 @@ func (x *ChangeExpiration) ProtoReflect() protoreflect.Message {
// Deprecated: Use ChangeExpiration.ProtoReflect.Descriptor instead. // Deprecated: Use ChangeExpiration.ProtoReflect.Descriptor instead.
func (*ChangeExpiration) Descriptor() ([]byte, []int) { func (*ChangeExpiration) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{7} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{8}
} }
func (x *ChangeExpiration) GetNewExpiration() *timestamppb.Timestamp { func (x *ChangeExpiration) GetNewExpiration() *timestamppb.Timestamp {
@ -493,7 +580,7 @@ type UpdateUserResponse struct {
func (x *UpdateUserResponse) Reset() { func (x *UpdateUserResponse) Reset() {
*x = UpdateUserResponse{} *x = UpdateUserResponse{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -506,7 +593,7 @@ func (x *UpdateUserResponse) String() string {
func (*UpdateUserResponse) ProtoMessage() {} func (*UpdateUserResponse) ProtoMessage() {}
func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -519,7 +606,7 @@ func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead. // Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead.
func (*UpdateUserResponse) Descriptor() ([]byte, []int) { func (*UpdateUserResponse) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{8} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{9}
} }
///////////////// /////////////////
@ -537,7 +624,7 @@ type DeleteUserRequest struct {
func (x *DeleteUserRequest) Reset() { func (x *DeleteUserRequest) Reset() {
*x = DeleteUserRequest{} *x = DeleteUserRequest{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -550,7 +637,7 @@ func (x *DeleteUserRequest) String() string {
func (*DeleteUserRequest) ProtoMessage() {} func (*DeleteUserRequest) ProtoMessage() {}
func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message { func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -563,7 +650,7 @@ func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteUserRequest.ProtoReflect.Descriptor instead. // Deprecated: Use DeleteUserRequest.ProtoReflect.Descriptor instead.
func (*DeleteUserRequest) Descriptor() ([]byte, []int) { func (*DeleteUserRequest) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{9} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{10}
} }
func (x *DeleteUserRequest) GetUsername() string { func (x *DeleteUserRequest) GetUsername() string {
@ -589,7 +676,7 @@ type DeleteUserResponse struct {
func (x *DeleteUserResponse) Reset() { func (x *DeleteUserResponse) Reset() {
*x = DeleteUserResponse{} *x = DeleteUserResponse{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -602,7 +689,7 @@ func (x *DeleteUserResponse) String() string {
func (*DeleteUserResponse) ProtoMessage() {} func (*DeleteUserResponse) ProtoMessage() {}
func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message { func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -615,7 +702,7 @@ func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteUserResponse.ProtoReflect.Descriptor instead. // Deprecated: Use DeleteUserResponse.ProtoReflect.Descriptor instead.
func (*DeleteUserResponse) Descriptor() ([]byte, []int) { func (*DeleteUserResponse) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{10} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{11}
} }
///////////////// /////////////////
@ -632,7 +719,7 @@ type TypeResponse struct {
func (x *TypeResponse) Reset() { func (x *TypeResponse) Reset() {
*x = TypeResponse{} *x = TypeResponse{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -645,7 +732,7 @@ func (x *TypeResponse) String() string {
func (*TypeResponse) ProtoMessage() {} func (*TypeResponse) ProtoMessage() {}
func (x *TypeResponse) ProtoReflect() protoreflect.Message { func (x *TypeResponse) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -658,7 +745,7 @@ func (x *TypeResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use TypeResponse.ProtoReflect.Descriptor instead. // Deprecated: Use TypeResponse.ProtoReflect.Descriptor instead.
func (*TypeResponse) Descriptor() ([]byte, []int) { func (*TypeResponse) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{11} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{12}
} }
func (x *TypeResponse) GetType() string { func (x *TypeResponse) GetType() string {
@ -682,7 +769,7 @@ type Statements struct {
func (x *Statements) Reset() { func (x *Statements) Reset() {
*x = Statements{} *x = Statements{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -695,7 +782,7 @@ func (x *Statements) String() string {
func (*Statements) ProtoMessage() {} func (*Statements) ProtoMessage() {}
func (x *Statements) ProtoReflect() protoreflect.Message { func (x *Statements) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -708,7 +795,7 @@ func (x *Statements) ProtoReflect() protoreflect.Message {
// Deprecated: Use Statements.ProtoReflect.Descriptor instead. // Deprecated: Use Statements.ProtoReflect.Descriptor instead.
func (*Statements) Descriptor() ([]byte, []int) { func (*Statements) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{12} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{13}
} }
func (x *Statements) GetCommands() []string { func (x *Statements) GetCommands() []string {
@ -727,7 +814,7 @@ type Empty struct {
func (x *Empty) Reset() { func (x *Empty) Reset() {
*x = Empty{} *x = Empty{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -740,7 +827,7 @@ func (x *Empty) String() string {
func (*Empty) ProtoMessage() {} func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message { func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13] mi := &file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -753,7 +840,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message {
// Deprecated: Use Empty.ProtoReflect.Descriptor instead. // Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) { func (*Empty) Descriptor() ([]byte, []int) {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{13} return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP(), []int{14}
} }
var File_sdk_database_dbplugin_v5_proto_database_proto protoreflect.FileDescriptor var File_sdk_database_dbplugin_v5_proto_database_proto protoreflect.FileDescriptor
@ -779,7 +866,7 @@ var file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc = []byte{
0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb1, 0x02, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x55, 0x66, 0x69, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0xf9, 0x02, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x75, 0x73, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x75, 0x73,
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76,
@ -798,29 +885,47 @@ var file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc = []byte{
0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x53, 0x74, 0x61,
0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x12, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x12, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x50, 0x0a, 0x0e, 0x55, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63,
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06,
0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x4b, 0x65, 0x79, 0x22, 0x50, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x43,
0x0f, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73,
0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65,
0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c,
0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72,
0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
0x32, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x43, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x62, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75,
0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6c, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12,
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76,
0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x35, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b,
0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x76, 0x35,
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 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, 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, 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, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d,
@ -893,7 +998,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_rawDescGZIP() []byte {
return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescData return file_sdk_database_dbplugin_v5_proto_database_proto_rawDescData
} }
var file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
var file_sdk_database_dbplugin_v5_proto_database_proto_goTypes = []interface{}{ var file_sdk_database_dbplugin_v5_proto_database_proto_goTypes = []interface{}{
(*InitializeRequest)(nil), // 0: dbplugin.v5.InitializeRequest (*InitializeRequest)(nil), // 0: dbplugin.v5.InitializeRequest
(*InitializeResponse)(nil), // 1: dbplugin.v5.InitializeResponse (*InitializeResponse)(nil), // 1: dbplugin.v5.InitializeResponse
@ -902,46 +1007,49 @@ var file_sdk_database_dbplugin_v5_proto_database_proto_goTypes = []interface{}{
(*NewUserResponse)(nil), // 4: dbplugin.v5.NewUserResponse (*NewUserResponse)(nil), // 4: dbplugin.v5.NewUserResponse
(*UpdateUserRequest)(nil), // 5: dbplugin.v5.UpdateUserRequest (*UpdateUserRequest)(nil), // 5: dbplugin.v5.UpdateUserRequest
(*ChangePassword)(nil), // 6: dbplugin.v5.ChangePassword (*ChangePassword)(nil), // 6: dbplugin.v5.ChangePassword
(*ChangeExpiration)(nil), // 7: dbplugin.v5.ChangeExpiration (*ChangePublicKey)(nil), // 7: dbplugin.v5.ChangePublicKey
(*UpdateUserResponse)(nil), // 8: dbplugin.v5.UpdateUserResponse (*ChangeExpiration)(nil), // 8: dbplugin.v5.ChangeExpiration
(*DeleteUserRequest)(nil), // 9: dbplugin.v5.DeleteUserRequest (*UpdateUserResponse)(nil), // 9: dbplugin.v5.UpdateUserResponse
(*DeleteUserResponse)(nil), // 10: dbplugin.v5.DeleteUserResponse (*DeleteUserRequest)(nil), // 10: dbplugin.v5.DeleteUserRequest
(*TypeResponse)(nil), // 11: dbplugin.v5.TypeResponse (*DeleteUserResponse)(nil), // 11: dbplugin.v5.DeleteUserResponse
(*Statements)(nil), // 12: dbplugin.v5.Statements (*TypeResponse)(nil), // 12: dbplugin.v5.TypeResponse
(*Empty)(nil), // 13: dbplugin.v5.Empty (*Statements)(nil), // 13: dbplugin.v5.Statements
(*structpb.Struct)(nil), // 14: google.protobuf.Struct (*Empty)(nil), // 14: dbplugin.v5.Empty
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp (*structpb.Struct)(nil), // 15: google.protobuf.Struct
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp
} }
var file_sdk_database_dbplugin_v5_proto_database_proto_depIdxs = []int32{ var file_sdk_database_dbplugin_v5_proto_database_proto_depIdxs = []int32{
14, // 0: dbplugin.v5.InitializeRequest.config_data:type_name -> google.protobuf.Struct 15, // 0: dbplugin.v5.InitializeRequest.config_data:type_name -> google.protobuf.Struct
14, // 1: dbplugin.v5.InitializeResponse.config_data:type_name -> google.protobuf.Struct 15, // 1: dbplugin.v5.InitializeResponse.config_data:type_name -> google.protobuf.Struct
3, // 2: dbplugin.v5.NewUserRequest.username_config:type_name -> dbplugin.v5.UsernameConfig 3, // 2: dbplugin.v5.NewUserRequest.username_config:type_name -> dbplugin.v5.UsernameConfig
15, // 3: dbplugin.v5.NewUserRequest.expiration:type_name -> google.protobuf.Timestamp 16, // 3: dbplugin.v5.NewUserRequest.expiration:type_name -> google.protobuf.Timestamp
12, // 4: dbplugin.v5.NewUserRequest.statements:type_name -> dbplugin.v5.Statements 13, // 4: dbplugin.v5.NewUserRequest.statements:type_name -> dbplugin.v5.Statements
12, // 5: dbplugin.v5.NewUserRequest.rollback_statements:type_name -> dbplugin.v5.Statements 13, // 5: dbplugin.v5.NewUserRequest.rollback_statements:type_name -> dbplugin.v5.Statements
6, // 6: dbplugin.v5.UpdateUserRequest.password:type_name -> dbplugin.v5.ChangePassword 6, // 6: dbplugin.v5.UpdateUserRequest.password:type_name -> dbplugin.v5.ChangePassword
7, // 7: dbplugin.v5.UpdateUserRequest.expiration:type_name -> dbplugin.v5.ChangeExpiration 8, // 7: dbplugin.v5.UpdateUserRequest.expiration:type_name -> dbplugin.v5.ChangeExpiration
12, // 8: dbplugin.v5.ChangePassword.statements:type_name -> dbplugin.v5.Statements 7, // 8: dbplugin.v5.UpdateUserRequest.public_key:type_name -> dbplugin.v5.ChangePublicKey
15, // 9: dbplugin.v5.ChangeExpiration.new_expiration:type_name -> google.protobuf.Timestamp 13, // 9: dbplugin.v5.ChangePassword.statements:type_name -> dbplugin.v5.Statements
12, // 10: dbplugin.v5.ChangeExpiration.statements:type_name -> dbplugin.v5.Statements 13, // 10: dbplugin.v5.ChangePublicKey.statements:type_name -> dbplugin.v5.Statements
12, // 11: dbplugin.v5.DeleteUserRequest.statements:type_name -> dbplugin.v5.Statements 16, // 11: dbplugin.v5.ChangeExpiration.new_expiration:type_name -> google.protobuf.Timestamp
0, // 12: dbplugin.v5.Database.Initialize:input_type -> dbplugin.v5.InitializeRequest 13, // 12: dbplugin.v5.ChangeExpiration.statements:type_name -> dbplugin.v5.Statements
2, // 13: dbplugin.v5.Database.NewUser:input_type -> dbplugin.v5.NewUserRequest 13, // 13: dbplugin.v5.DeleteUserRequest.statements:type_name -> dbplugin.v5.Statements
5, // 14: dbplugin.v5.Database.UpdateUser:input_type -> dbplugin.v5.UpdateUserRequest 0, // 14: dbplugin.v5.Database.Initialize:input_type -> dbplugin.v5.InitializeRequest
9, // 15: dbplugin.v5.Database.DeleteUser:input_type -> dbplugin.v5.DeleteUserRequest 2, // 15: dbplugin.v5.Database.NewUser:input_type -> dbplugin.v5.NewUserRequest
13, // 16: dbplugin.v5.Database.Type:input_type -> dbplugin.v5.Empty 5, // 16: dbplugin.v5.Database.UpdateUser:input_type -> dbplugin.v5.UpdateUserRequest
13, // 17: dbplugin.v5.Database.Close:input_type -> dbplugin.v5.Empty 10, // 17: dbplugin.v5.Database.DeleteUser:input_type -> dbplugin.v5.DeleteUserRequest
1, // 18: dbplugin.v5.Database.Initialize:output_type -> dbplugin.v5.InitializeResponse 14, // 18: dbplugin.v5.Database.Type:input_type -> dbplugin.v5.Empty
4, // 19: dbplugin.v5.Database.NewUser:output_type -> dbplugin.v5.NewUserResponse 14, // 19: dbplugin.v5.Database.Close:input_type -> dbplugin.v5.Empty
8, // 20: dbplugin.v5.Database.UpdateUser:output_type -> dbplugin.v5.UpdateUserResponse 1, // 20: dbplugin.v5.Database.Initialize:output_type -> dbplugin.v5.InitializeResponse
10, // 21: dbplugin.v5.Database.DeleteUser:output_type -> dbplugin.v5.DeleteUserResponse 4, // 21: dbplugin.v5.Database.NewUser:output_type -> dbplugin.v5.NewUserResponse
11, // 22: dbplugin.v5.Database.Type:output_type -> dbplugin.v5.TypeResponse 9, // 22: dbplugin.v5.Database.UpdateUser:output_type -> dbplugin.v5.UpdateUserResponse
13, // 23: dbplugin.v5.Database.Close:output_type -> dbplugin.v5.Empty 11, // 23: dbplugin.v5.Database.DeleteUser:output_type -> dbplugin.v5.DeleteUserResponse
18, // [18:24] is the sub-list for method output_type 12, // 24: dbplugin.v5.Database.Type:output_type -> dbplugin.v5.TypeResponse
12, // [12:18] is the sub-list for method input_type 14, // 25: dbplugin.v5.Database.Close:output_type -> dbplugin.v5.Empty
12, // [12:12] is the sub-list for extension type_name 20, // [20:26] is the sub-list for method output_type
12, // [12:12] is the sub-list for extension extendee 14, // [14:20] is the sub-list for method input_type
0, // [0:12] is the sub-list for field type_name 14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
} }
func init() { file_sdk_database_dbplugin_v5_proto_database_proto_init() } func init() { file_sdk_database_dbplugin_v5_proto_database_proto_init() }
@ -1035,7 +1143,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChangeExpiration); i { switch v := v.(*ChangePublicKey); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1047,7 +1155,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UpdateUserResponse); i { switch v := v.(*ChangeExpiration); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1059,7 +1167,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteUserRequest); i { switch v := v.(*UpdateUserResponse); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1071,7 +1179,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteUserResponse); i { switch v := v.(*DeleteUserRequest); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1083,7 +1191,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TypeResponse); i { switch v := v.(*DeleteUserResponse); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1095,7 +1203,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Statements); i { switch v := v.(*TypeResponse); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1107,6 +1215,18 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
} }
} }
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Statements); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_sdk_database_dbplugin_v5_proto_database_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i { switch v := v.(*Empty); i {
case 0: case 0:
return &v.state return &v.state
@ -1125,7 +1245,7 @@ func file_sdk_database_dbplugin_v5_proto_database_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc, RawDescriptor: file_sdk_database_dbplugin_v5_proto_database_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 14, NumMessages: 15,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -27,6 +27,8 @@ message NewUserRequest {
google.protobuf.Timestamp expiration = 3; google.protobuf.Timestamp expiration = 3;
Statements statements = 4; Statements statements = 4;
Statements rollback_statements = 5; Statements rollback_statements = 5;
int32 credential_type = 6;
bytes public_key = 7;
} }
message UsernameConfig { message UsernameConfig {
@ -45,6 +47,8 @@ message UpdateUserRequest {
string username = 1; string username = 1;
ChangePassword password = 2; ChangePassword password = 2;
ChangeExpiration expiration = 3; ChangeExpiration expiration = 3;
ChangePublicKey public_key = 4;
int32 credential_type = 5;
} }
message ChangePassword { message ChangePassword {
@ -52,6 +56,11 @@ message ChangePassword {
Statements statements = 2; Statements statements = 2;
} }
message ChangePublicKey {
bytes new_public_key = 1;
Statements statements = 2;
}
message ChangeExpiration { message ChangeExpiration {
google.protobuf.Timestamp new_expiration = 1; google.protobuf.Timestamp new_expiration = 1;
Statements statements = 2; Statements statements = 2;