1888323243
This is part 1 of 4 for renaming the `newdbplugin` package. This copies the existing package to the new location but keeps the current one in place so we can migrate the existing references over more easily.
192 lines
5.7 KiB
Go
192 lines
5.7 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/strutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func pathCredsCreate(b *databaseBackend) []*framework.Path {
|
|
return []*framework.Path{
|
|
&framework.Path{
|
|
Pattern: "creds/" + framework.GenericNameRegex("name"),
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"name": &framework.FieldSchema{
|
|
Type: framework.TypeString,
|
|
Description: "Name of the role.",
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.ReadOperation: b.pathCredsCreateRead(),
|
|
},
|
|
|
|
HelpSynopsis: pathCredsCreateReadHelpSyn,
|
|
HelpDescription: pathCredsCreateReadHelpDesc,
|
|
},
|
|
&framework.Path{
|
|
Pattern: "static-creds/" + framework.GenericNameRegex("name"),
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"name": &framework.FieldSchema{
|
|
Type: framework.TypeString,
|
|
Description: "Name of the static role.",
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.ReadOperation: b.pathStaticCredsRead(),
|
|
},
|
|
|
|
HelpSynopsis: pathStaticCredsReadHelpSyn,
|
|
HelpDescription: pathStaticCredsReadHelpDesc,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc {
|
|
return func(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
|
|
}
|
|
|
|
dbConfig, err := b.DatabaseConfig(ctx, req.Storage, role.DBName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If role name isn't in the database's allowed roles, send back a
|
|
// permission denied.
|
|
if !strutil.StrListContains(dbConfig.AllowedRoles, "*") && !strutil.StrListContainsGlob(dbConfig.AllowedRoles, name) {
|
|
return nil, fmt.Errorf("%q is not an allowed role", name)
|
|
}
|
|
|
|
// Get the Database object
|
|
dbi, err := b.GetConnection(ctx, req.Storage, role.DBName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dbi.RLock()
|
|
defer dbi.RUnlock()
|
|
|
|
ttl, _, err := framework.CalculateTTL(b.System(), 0, role.DefaultTTL, 0, role.MaxTTL, 0, time.Time{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expiration := time.Now().Add(ttl)
|
|
// Adding a small buffer since the TTL will be calculated again after this call
|
|
// to ensure the database credential does not expire before the lease
|
|
expiration = expiration.Add(5 * time.Second)
|
|
|
|
password, err := dbi.database.GeneratePassword(ctx, b.System(), dbConfig.PasswordPolicy)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to generate password: %w", err)
|
|
}
|
|
|
|
newUserReq := v5.NewUserRequest{
|
|
UsernameConfig: v5.UsernameMetadata{
|
|
DisplayName: req.DisplayName,
|
|
RoleName: name,
|
|
},
|
|
Statements: v5.Statements{
|
|
Commands: role.Statements.Creation,
|
|
},
|
|
RollbackStatements: v5.Statements{
|
|
Commands: role.Statements.Rollback,
|
|
},
|
|
Password: password,
|
|
Expiration: expiration,
|
|
}
|
|
|
|
// 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)
|
|
if err != nil {
|
|
b.CloseIfShutdown(dbi, err)
|
|
return nil, err
|
|
}
|
|
|
|
respData := map[string]interface{}{
|
|
"username": newUserResp.Username,
|
|
"password": password,
|
|
}
|
|
internal := map[string]interface{}{
|
|
"username": newUserResp.Username,
|
|
"role": name,
|
|
"db_name": role.DBName,
|
|
"revocation_statements": role.Statements.Revocation,
|
|
}
|
|
resp := b.Secret(SecretCredsType).Response(respData, internal)
|
|
resp.Secret.TTL = role.DefaultTTL
|
|
resp.Secret.MaxTTL = role.MaxTTL
|
|
return resp, nil
|
|
}
|
|
}
|
|
|
|
func (b *databaseBackend) pathStaticCredsRead() framework.OperationFunc {
|
|
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
name := data.Get("name").(string)
|
|
|
|
role, err := b.StaticRole(ctx, req.Storage, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if role == nil {
|
|
return logical.ErrorResponse("unknown role: %s", name), nil
|
|
}
|
|
|
|
dbConfig, err := b.DatabaseConfig(ctx, req.Storage, role.DBName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If role name isn't in the database's allowed roles, send back a
|
|
// permission denied.
|
|
if !strutil.StrListContains(dbConfig.AllowedRoles, "*") && !strutil.StrListContainsGlob(dbConfig.AllowedRoles, name) {
|
|
return nil, fmt.Errorf("%q is not an allowed role", name)
|
|
}
|
|
|
|
return &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"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
|
|
}
|
|
}
|
|
|
|
const pathCredsCreateReadHelpSyn = `
|
|
Request database credentials for a certain role.
|
|
`
|
|
|
|
const pathCredsCreateReadHelpDesc = `
|
|
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.
|
|
`
|
|
|
|
const pathStaticCredsReadHelpSyn = `
|
|
Request database credentials for a certain static role. These credentials are
|
|
rotated periodically.
|
|
`
|
|
|
|
const pathStaticCredsReadHelpDesc = `
|
|
This path reads database credentials for a certain static role. The database
|
|
credentials are rotated periodically according to their configuration, and will
|
|
return the same password until they are rotated.
|
|
`
|