aa6d61477e
VAULT-5827 Don't prepare SQL queries before executing them We don't support proper prepared statements, i.e., preparing once and executing many times since we do our own templating. So preparing our queries does not really accomplish anything, and can have severe performance impacts (see https://github.com/hashicorp/vault-plugin-database-snowflake/issues/13 for example). This behavior seems to have been copy-pasted for many years but not for any particular reason that we have been able to find. First use was in https://github.com/hashicorp/vault/pull/15 So here we switch to new methods suffixed with `Direct` to indicate that they don't `Prepare` before running `Exec`, and switch everything here to use those. We maintain the older methods with the existing behavior (with `Prepare`) for backwards compatibility.
130 lines
3 KiB
Go
130 lines
3 KiB
Go
package mssql
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
|
uuid "github.com/hashicorp/go-uuid"
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func pathCredsCreate(b *backend) *framework.Path {
|
|
return &framework.Path{
|
|
Pattern: "creds/" + framework.GenericNameRegex("name"),
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"name": {
|
|
Type: framework.TypeString,
|
|
Description: "Name of the role.",
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.ReadOperation: b.pathCredsCreateRead,
|
|
},
|
|
|
|
HelpSynopsis: pathCredsCreateHelpSyn,
|
|
HelpDescription: pathCredsCreateHelpDesc,
|
|
}
|
|
}
|
|
|
|
func (b *backend) pathCredsCreateRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
name := data.Get("name").(string)
|
|
|
|
// Get the role
|
|
role, err := b.Role(ctx, req.Storage, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if role == nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("unknown role: %s", name)), nil
|
|
}
|
|
|
|
// Determine if we have a lease configuration
|
|
leaseConfig, err := b.LeaseConfig(ctx, req.Storage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if leaseConfig == nil {
|
|
leaseConfig = &configLease{}
|
|
}
|
|
|
|
// Generate our username and password
|
|
displayName := req.DisplayName
|
|
if len(displayName) > 10 {
|
|
displayName = displayName[:10]
|
|
}
|
|
userUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
username := fmt.Sprintf("%s-%s", displayName, userUUID)
|
|
password, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get our handle
|
|
db, err := b.DB(ctx, req.Storage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Start a transaction
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Always reset database to default db of connection. Since it is in a
|
|
// transaction, all statements will be on the same connection in the pool.
|
|
roleSQL := fmt.Sprintf("USE [%s]; %s", b.defaultDb, role.SQL)
|
|
|
|
// Execute each query
|
|
for _, query := range strutil.ParseArbitraryStringSlice(roleSQL, ";") {
|
|
query = strings.TrimSpace(query)
|
|
if len(query) == 0 {
|
|
continue
|
|
}
|
|
|
|
m := map[string]string{
|
|
"name": username,
|
|
"password": password,
|
|
}
|
|
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, query); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Commit the transaction
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Return the secret
|
|
resp := b.Secret(SecretCredsType).Response(map[string]interface{}{
|
|
"username": username,
|
|
"password": password,
|
|
}, map[string]interface{}{
|
|
"username": username,
|
|
})
|
|
resp.Secret.TTL = leaseConfig.TTL
|
|
resp.Secret.MaxTTL = leaseConfig.TTLMax
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
const pathCredsCreateHelpSyn = `
|
|
Request database credentials for a certain role.
|
|
`
|
|
|
|
const pathCredsCreateHelpDesc = `
|
|
This path reads database credentials for a certain role. The
|
|
database credentials will be generated on demand and will be automatically
|
|
revoked when the lease is up.
|
|
`
|