secrets/database: adds ability to manage alternative credential types and configuration (#15376)
This commit is contained in:
parent
60acf9ad6e
commit
d3629ab49d
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
||||||
|
|
|
@ -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
2
go.mod
|
@ -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
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue