mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
MongoDB - add username customization (#10858)
This commit is contained in:
@@ -7,9 +7,11 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/credsutil"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/template"
|
||||
|
||||
dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@@ -18,11 +20,17 @@ import (
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
|
||||
)
|
||||
|
||||
const mongoDBTypeName = "mongodb"
|
||||
const (
|
||||
mongoDBTypeName = "mongodb"
|
||||
|
||||
defaultUserNameTemplate = `{{ printf "v-%s-%s-%s-%s" (.DisplayName | truncate 15) (.RoleName | truncate 15) (random 20) (unix_time) | truncate 100 }}`
|
||||
)
|
||||
|
||||
// MongoDB is an implementation of Database interface
|
||||
type MongoDB struct {
|
||||
*mongoDBConnectionProducer
|
||||
|
||||
usernameProducer template.StringTemplate
|
||||
}
|
||||
|
||||
var _ dbplugin.Database = &MongoDB{}
|
||||
@@ -64,7 +72,26 @@ func (m *MongoDB) Initialize(ctx context.Context, req dbplugin.InitializeRequest
|
||||
|
||||
m.RawConfig = req.Config
|
||||
|
||||
err := mapstructure.WeakDecode(req.Config, m.mongoDBConnectionProducer)
|
||||
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: %w", err)
|
||||
}
|
||||
|
||||
err = mapstructure.WeakDecode(req.Config, m.mongoDBConnectionProducer)
|
||||
if err != nil {
|
||||
return dbplugin.InitializeResponse{}, err
|
||||
}
|
||||
@@ -116,12 +143,7 @@ func (m *MongoDB) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (dbp
|
||||
return dbplugin.NewUserResponse{}, dbutil.ErrEmptyCreationStatement
|
||||
}
|
||||
|
||||
username, err := credsutil.GenerateUsername(
|
||||
credsutil.DisplayName(req.UsernameConfig.DisplayName, 15),
|
||||
credsutil.RoleName(req.UsernameConfig.RoleName, 15),
|
||||
credsutil.MaxLength(100),
|
||||
credsutil.Separator("-"),
|
||||
)
|
||||
username, err := m.usernameProducer.Generate(req.UsernameConfig)
|
||||
if err != nil {
|
||||
return dbplugin.NewUserResponse{}, err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/hashicorp/vault/helper/testhelpers/mongodb"
|
||||
dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/mongo/readpref"
|
||||
@@ -51,6 +52,78 @@ func TestMongoDB_Initialize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewUser_usernameTemplate(t *testing.T) {
|
||||
type testCase struct {
|
||||
usernameTemplate string
|
||||
|
||||
newUserReq dbplugin.NewUserRequest
|
||||
expectedUsernameRegex string
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"default username template": {
|
||||
usernameTemplate: "",
|
||||
|
||||
newUserReq: dbplugin.NewUserRequest{
|
||||
UsernameConfig: dbplugin.UsernameMetadata{
|
||||
DisplayName: "token",
|
||||
RoleName: "testrolenamewithmanycharacters",
|
||||
},
|
||||
Statements: dbplugin.Statements{
|
||||
Commands: []string{mongoAdminRole},
|
||||
},
|
||||
Password: "98yq3thgnakjsfhjkl",
|
||||
Expiration: time.Now().Add(time.Minute),
|
||||
},
|
||||
|
||||
expectedUsernameRegex: "^v-token-testrolenamewit-[a-zA-Z0-9]{20}-[0-9]{10}$",
|
||||
},
|
||||
"custom username template": {
|
||||
usernameTemplate: "{{random 2 | uppercase}}_{{unix_time}}_{{.RoleName | uppercase}}_{{.DisplayName | uppercase}}",
|
||||
|
||||
newUserReq: dbplugin.NewUserRequest{
|
||||
UsernameConfig: dbplugin.UsernameMetadata{
|
||||
DisplayName: "token",
|
||||
RoleName: "testrolenamewithmanycharacters",
|
||||
},
|
||||
Statements: dbplugin.Statements{
|
||||
Commands: []string{mongoAdminRole},
|
||||
},
|
||||
Password: "98yq3thgnakjsfhjkl",
|
||||
Expiration: time.Now().Add(time.Minute),
|
||||
},
|
||||
|
||||
expectedUsernameRegex: "^[A-Z0-9]{2}_[0-9]{10}_TESTROLENAMEWITHMANYCHARACTERS_TOKEN$",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
cleanup, connURL := mongodb.PrepareTestContainer(t, "latest")
|
||||
defer cleanup()
|
||||
|
||||
db := new()
|
||||
defer dbtesting.AssertClose(t, db)
|
||||
|
||||
initReq := dbplugin.InitializeRequest{
|
||||
Config: map[string]interface{}{
|
||||
"connection_url": connURL,
|
||||
"username_template": test.usernameTemplate,
|
||||
},
|
||||
VerifyConnection: true,
|
||||
}
|
||||
dbtesting.AssertInitialize(t, db, initReq)
|
||||
|
||||
ctx := context.Background()
|
||||
newUserResp, err := db.NewUser(ctx, test.newUserReq)
|
||||
require.NoError(t, err)
|
||||
require.Regexp(t, test.expectedUsernameRegex, newUserResp.Username)
|
||||
|
||||
assertCredsExist(t, newUserResp.Username, test.newUserReq.Password, connURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMongoDB_CreateUser(t *testing.T) {
|
||||
cleanup, connURL := mongodb.PrepareTestContainer(t, "latest")
|
||||
defer cleanup()
|
||||
|
||||
Reference in New Issue
Block a user