mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 10:12:35 +00:00
MSSQL - Add username customization (#10767)
This commit is contained in:
@@ -12,19 +12,25 @@ import (
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/connutil"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/credsutil"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/template"
|
||||
)
|
||||
|
||||
const msSQLTypeName = "mssql"
|
||||
const (
|
||||
msSQLTypeName = "mssql"
|
||||
|
||||
defaultUserNameTemplate = `{{ printf "v-%s-%s-%s-%s" (.DisplayName | truncate 20) (.RoleName | truncate 20) (random 20) (unix_time) | truncate 128 }}`
|
||||
)
|
||||
|
||||
var _ dbplugin.Database = &MSSQL{}
|
||||
|
||||
// MSSQL is an implementation of Database interface
|
||||
type MSSQL struct {
|
||||
*connutil.SQLConnectionProducer
|
||||
|
||||
usernameProducer template.StringTemplate
|
||||
}
|
||||
|
||||
func New() (interface{}, error) {
|
||||
@@ -69,6 +75,26 @@ func (m *MSSQL) Initialize(ctx context.Context, req dbplugin.InitializeRequest)
|
||||
if err != nil {
|
||||
return dbplugin.InitializeResponse{}, err
|
||||
}
|
||||
|
||||
usernameTemplate, err := strutil.GetString(req.Config, "username_template")
|
||||
if err != nil {
|
||||
return dbplugin.InitializeResponse{}, fmt.Errorf("failed to retrieve username_template: %w", err)
|
||||
}
|
||||
if usernameTemplate == "" {
|
||||
usernameTemplate = defaultUserNameTemplate
|
||||
}
|
||||
|
||||
up, err := template.NewTemplate(template.Template(usernameTemplate))
|
||||
if err != nil {
|
||||
return dbplugin.InitializeResponse{}, fmt.Errorf("unable to initialize username template: %w", err)
|
||||
}
|
||||
m.usernameProducer = up
|
||||
|
||||
_, err = m.usernameProducer.Generate(dbplugin.UsernameMetadata{})
|
||||
if err != nil {
|
||||
return dbplugin.InitializeResponse{}, fmt.Errorf("invalid username template - did you reference a field that isn't available? : %w", err)
|
||||
}
|
||||
|
||||
resp := dbplugin.InitializeResponse{
|
||||
Config: newConf,
|
||||
}
|
||||
@@ -90,12 +116,7 @@ func (m *MSSQL) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (dbplu
|
||||
return dbplugin.NewUserResponse{}, dbutil.ErrEmptyCreationStatement
|
||||
}
|
||||
|
||||
username, err := credsutil.GenerateUsername(
|
||||
credsutil.DisplayName(req.UsernameConfig.DisplayName, 20),
|
||||
credsutil.RoleName(req.UsernameConfig.RoleName, 20),
|
||||
credsutil.MaxLength(128),
|
||||
credsutil.Separator("-"),
|
||||
)
|
||||
username, err := m.usernameProducer.Generate(req.UsernameConfig)
|
||||
if err != nil {
|
||||
return dbplugin.NewUserResponse{}, err
|
||||
}
|
||||
|
||||
@@ -62,10 +62,11 @@ func TestNewUser(t *testing.T) {
|
||||
defer cleanup()
|
||||
|
||||
type testCase struct {
|
||||
req dbplugin.NewUserRequest
|
||||
usernameRegex string
|
||||
expectErr bool
|
||||
assertUser func(t testing.TB, connURL, username, password string)
|
||||
usernameTemplate string
|
||||
req dbplugin.NewUserRequest
|
||||
usernameRegex string
|
||||
expectErr bool
|
||||
assertUser func(t testing.TB, connURL, username, password string)
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
@@ -99,6 +100,23 @@ func TestNewUser(t *testing.T) {
|
||||
expectErr: false,
|
||||
assertUser: assertCredsExist,
|
||||
},
|
||||
"custom username template": {
|
||||
usernameTemplate: "{{random 10}}_{{.RoleName}}.{{.DisplayName | sha256}}",
|
||||
req: dbplugin.NewUserRequest{
|
||||
UsernameConfig: dbplugin.UsernameMetadata{
|
||||
DisplayName: "tokenwithlotsofextracharactershere",
|
||||
RoleName: "myrolenamewithlotsofextracharacters",
|
||||
},
|
||||
Statements: dbplugin.Statements{
|
||||
Commands: []string{testMSSQLRole},
|
||||
},
|
||||
Password: "AG4qagho-dsvZ",
|
||||
Expiration: time.Now().Add(1 * time.Second),
|
||||
},
|
||||
usernameRegex: "^[a-zA-Z0-9]{10}_myrolenamewithlotsofextracharacters.80d15d22dba29ddbd4994f8009b5ff7b17922c267eb49fb805a9488bd55d11f9$",
|
||||
expectErr: false,
|
||||
assertUser: assertCredsExist,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
@@ -110,7 +128,8 @@ func TestNewUser(t *testing.T) {
|
||||
|
||||
initReq := dbplugin.InitializeRequest{
|
||||
Config: map[string]interface{}{
|
||||
"connection_url": connURL,
|
||||
"connection_url": connURL,
|
||||
"username_template": test.usernameTemplate,
|
||||
},
|
||||
VerifyConnection: true,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user