2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2016-12-19 18:15:58 +00:00
package database
import (
2017-12-14 22:03:11 +00:00
"context"
2017-04-13 00:35:02 +00:00
"errors"
2016-12-19 18:15:58 +00:00
"fmt"
2018-04-07 15:06:04 +00:00
"net/url"
2022-09-09 16:32:28 +00:00
"sort"
2016-12-19 18:15:58 +00:00
"github.com/fatih/structs"
2022-03-29 14:33:55 +00:00
"github.com/hashicorp/go-uuid"
2022-09-09 16:32:28 +00:00
"github.com/hashicorp/go-version"
2022-03-29 14:33:55 +00:00
2022-11-23 18:36:25 +00:00
"github.com/hashicorp/vault/helper/versions"
2020-10-15 19:20:12 +00:00
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/framework"
2022-09-09 16:32:28 +00:00
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/logical"
2016-12-19 18:15:58 +00:00
)
2017-04-13 00:35:02 +00:00
var (
2017-05-01 22:30:56 +00:00
respErrEmptyPluginName = "empty plugin name"
respErrEmptyName = "empty name attribute given"
2017-04-13 00:35:02 +00:00
)
2017-04-24 20:59:12 +00:00
// DatabaseConfig is used by the Factory function to configure a Database
2017-04-13 17:33:34 +00:00
// object.
type DatabaseConfig struct {
2022-09-09 16:32:28 +00:00
PluginName string ` json:"plugin_name" structs:"plugin_name" mapstructure:"plugin_name" `
PluginVersion string ` json:"plugin_version" structs:"plugin_version" mapstructure:"plugin_version" `
2017-04-13 17:33:34 +00:00
// ConnectionDetails stores the database specific connection settings needed
// by each database type.
ConnectionDetails map [ string ] interface { } ` json:"connection_details" structs:"connection_details" mapstructure:"connection_details" `
AllowedRoles [ ] string ` json:"allowed_roles" structs:"allowed_roles" mapstructure:"allowed_roles" `
2018-03-21 19:05:56 +00:00
RootCredentialsRotateStatements [ ] string ` json:"root_credentials_rotate_statements" structs:"root_credentials_rotate_statements" mapstructure:"root_credentials_rotate_statements" `
2020-09-18 21:10:54 +00:00
PasswordPolicy string ` json:"password_policy" structs:"password_policy" mapstructure:"password_policy" `
2017-04-13 17:33:34 +00:00
}
2022-05-17 16:21:26 +00:00
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
}
2017-04-11 18:50:34 +00:00
// pathResetConnection configures a path to reset a plugin.
2017-02-16 00:51:59 +00:00
func pathResetConnection ( b * databaseBackend ) * framework . Path {
return & framework . Path {
Pattern : fmt . Sprintf ( "reset/%s" , framework . GenericNameRegex ( "name" ) ) ,
2023-04-10 18:22:02 +00:00
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixDatabase ,
OperationVerb : "reset" ,
OperationSuffix : "connection" ,
} ,
2017-02-16 00:51:59 +00:00
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"name" : {
2017-02-16 00:51:59 +00:00
Type : framework . TypeString ,
2017-04-13 00:35:02 +00:00
Description : "Name of this database connection" ,
2017-02-16 00:51:59 +00:00
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2017-04-11 18:50:34 +00:00
logical . UpdateOperation : b . pathConnectionReset ( ) ,
2017-02-16 00:51:59 +00:00
} ,
2017-04-11 18:50:34 +00:00
HelpSynopsis : pathResetConnectionHelpSyn ,
HelpDescription : pathResetConnectionHelpDesc ,
2017-02-16 00:51:59 +00:00
}
}
2017-04-11 18:50:34 +00:00
// pathConnectionReset resets a plugin by closing the existing instance and
// creating a new one.
func ( b * databaseBackend ) pathConnectionReset ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-04-11 18:50:34 +00:00
name := data . Get ( "name" ) . ( string )
if name == "" {
2017-05-01 22:30:56 +00:00
return logical . ErrorResponse ( respErrEmptyName ) , nil
2017-04-11 18:50:34 +00:00
}
2017-04-13 00:35:02 +00:00
// Close plugin and delete the entry in the connections cache.
2018-03-21 19:05:56 +00:00
if err := b . ClearConnection ( name ) ; err != nil {
return nil , err
}
2017-02-16 00:51:59 +00:00
2017-04-13 00:35:02 +00:00
// Execute plugin again, we don't need the object so throw away.
2018-03-21 19:05:56 +00:00
if _ , err := b . GetConnection ( ctx , req . Storage , name ) ; err != nil {
2017-04-11 18:50:34 +00:00
return nil , err
}
2017-02-16 00:51:59 +00:00
2017-04-11 18:50:34 +00:00
return nil , nil
2017-03-13 21:39:55 +00:00
}
2017-02-16 00:51:59 +00:00
}
2017-03-22 00:19:30 +00:00
// pathConfigurePluginConnection returns a configured framework.Path setup to
// operate on plugins.
2017-03-10 05:31:29 +00:00
func pathConfigurePluginConnection ( b * databaseBackend ) * framework . Path {
2016-12-19 18:15:58 +00:00
return & framework . Path {
2017-04-11 01:38:34 +00:00
Pattern : fmt . Sprintf ( "config/%s" , framework . GenericNameRegex ( "name" ) ) ,
2023-04-10 18:22:02 +00:00
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixDatabase ,
} ,
2016-12-19 18:15:58 +00:00
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"name" : {
2016-12-19 18:15:58 +00:00
Type : framework . TypeString ,
2017-04-13 00:35:02 +00:00
Description : "Name of this database connection" ,
2016-12-19 18:15:58 +00:00
} ,
2021-04-08 16:43:39 +00:00
"plugin_name" : {
2017-03-10 22:10:42 +00:00
Type : framework . TypeString ,
2017-04-11 18:50:34 +00:00
Description : ` The name of a builtin or previously registered
2017-04-13 17:33:34 +00:00
plugin known to vault . This endpoint will create an instance of
that plugin type . ` ,
2017-03-10 22:10:42 +00:00
} ,
2017-04-13 00:35:02 +00:00
2022-09-09 16:32:28 +00:00
"plugin_version" : {
Type : framework . TypeString ,
Description : ` The version of the plugin to use. ` ,
} ,
2021-04-08 16:43:39 +00:00
"verify_connection" : {
2017-04-13 00:35:02 +00:00
Type : framework . TypeBool ,
Default : true ,
Description : ` If true , the connection details are verified by
2017-04-13 17:33:34 +00:00
actually connecting to the database . Defaults to true . ` ,
} ,
2021-04-08 16:43:39 +00:00
"allowed_roles" : {
2017-04-25 17:26:23 +00:00
Type : framework . TypeCommaStringSlice ,
Description : ` Comma separated string or array of the role names
2017-04-25 18:48:24 +00:00
allowed to get creds from this database connection . If empty no
roles are allowed . If "*" all roles are allowed . ` ,
2017-04-13 00:35:02 +00:00
} ,
2018-03-21 19:05:56 +00:00
2021-04-08 16:43:39 +00:00
"root_rotation_statements" : {
2018-03-21 19:05:56 +00:00
Type : framework . TypeStringSlice ,
Description : ` Specifies the database statements to be executed
to rotate the root user ' s credentials . See the plugin ' s API
page for more information on support and formatting for this
parameter . ` ,
} ,
2021-04-08 16:43:39 +00:00
"password_policy" : {
2020-09-18 21:10:54 +00:00
Type : framework . TypeString ,
Description : ` Password policy to use when generating passwords. ` ,
} ,
2016-12-19 18:15:58 +00:00
} ,
2018-06-19 15:24:28 +00:00
ExistenceCheck : b . connectionExistenceCheck ( ) ,
2023-04-10 18:22:02 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . CreateOperation : & framework . PathOperation {
Callback : b . connectionWriteHandler ( ) ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "configure" ,
OperationSuffix : "connection" ,
} ,
} ,
logical . UpdateOperation : & framework . PathOperation {
Callback : b . connectionWriteHandler ( ) ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "configure" ,
OperationSuffix : "connection" ,
} ,
} ,
logical . ReadOperation : & framework . PathOperation {
Callback : b . connectionReadHandler ( ) ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "read" ,
OperationSuffix : "connection-configuration" ,
} ,
} ,
logical . DeleteOperation : & framework . PathOperation {
Callback : b . connectionDeleteHandler ( ) ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "delete" ,
OperationSuffix : "connection-configuration" ,
} ,
} ,
2016-12-19 18:15:58 +00:00
} ,
HelpSynopsis : pathConfigConnectionHelpSyn ,
HelpDescription : pathConfigConnectionHelpDesc ,
}
}
2018-06-19 15:24:28 +00:00
func ( b * databaseBackend ) connectionExistenceCheck ( ) framework . ExistenceFunc {
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( bool , error ) {
name := data . Get ( "name" ) . ( string )
if name == "" {
return false , errors . New ( ` missing "name" parameter ` )
}
entry , err := req . Storage . Get ( ctx , fmt . Sprintf ( "config/%s" , name ) )
if err != nil {
2020-09-18 21:10:54 +00:00
return false , fmt . Errorf ( "failed to read connection configuration: %w" , err )
2018-06-19 15:24:28 +00:00
}
return entry != nil , nil
}
}
2017-06-07 14:03:17 +00:00
func pathListPluginConnection ( b * databaseBackend ) * framework . Path {
return & framework . Path {
Pattern : fmt . Sprintf ( "config/?$" ) ,
2023-04-10 18:22:02 +00:00
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixDatabase ,
OperationSuffix : "connections" ,
} ,
2017-06-07 14:03:17 +00:00
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : b . connectionListHandler ( ) ,
} ,
HelpSynopsis : pathConfigConnectionHelpSyn ,
HelpDescription : pathConfigConnectionHelpDesc ,
}
}
func ( b * databaseBackend ) connectionListHandler ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
entries , err := req . Storage . List ( ctx , "config/" )
2017-06-07 14:03:17 +00:00
if err != nil {
return nil , err
}
return logical . ListResponse ( entries ) , nil
}
}
2017-05-04 00:37:34 +00:00
// connectionReadHandler reads out the connection configuration
2017-03-10 05:31:29 +00:00
func ( b * databaseBackend ) connectionReadHandler ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-03-10 05:31:29 +00:00
name := data . Get ( "name" ) . ( string )
2017-04-13 00:35:02 +00:00
if name == "" {
2017-05-01 22:30:56 +00:00
return logical . ErrorResponse ( respErrEmptyName ) , nil
2017-04-13 00:35:02 +00:00
}
2016-12-19 18:15:58 +00:00
2018-01-19 06:44:44 +00:00
entry , err := req . Storage . Get ( ctx , fmt . Sprintf ( "config/%s" , name ) )
2017-03-10 05:31:29 +00:00
if err != nil {
2020-09-18 21:10:54 +00:00
return nil , fmt . Errorf ( "failed to read connection configuration: %w" , err )
2017-03-10 05:31:29 +00:00
}
if entry == nil {
return nil , nil
}
2016-12-19 18:15:58 +00:00
2017-04-05 23:20:31 +00:00
var config DatabaseConfig
2017-03-10 05:31:29 +00:00
if err := entry . DecodeJSON ( & config ) ; err != nil {
return nil , err
}
2018-03-30 14:17:39 +00:00
2022-03-29 14:33:55 +00:00
// Ensure that we only ever include a redacted valid URL in the response.
2018-04-07 15:06:04 +00:00
if connURLRaw , ok := config . ConnectionDetails [ "connection_url" ] ; ok {
2022-03-29 14:33:55 +00:00
if p , err := url . Parse ( connURLRaw . ( string ) ) ; err == nil {
config . ConnectionDetails [ "connection_url" ] = p . Redacted ( )
2018-04-07 15:06:04 +00:00
}
2018-03-30 14:17:39 +00:00
}
2022-11-23 18:36:25 +00:00
if versions . IsBuiltinVersion ( config . PluginVersion ) {
// This gets treated as though it's empty when mounting, and will get
// overwritten to be empty when the config is next written. See #18051.
config . PluginVersion = ""
}
2018-04-07 15:06:04 +00:00
delete ( config . ConnectionDetails , "password" )
2020-11-19 10:58:55 +00:00
delete ( config . ConnectionDetails , "private_key" )
2018-04-07 15:06:04 +00:00
2017-03-10 05:31:29 +00:00
return & logical . Response {
Data : structs . New ( config ) . Map ( ) ,
} , nil
2016-12-19 18:15:58 +00:00
}
}
2017-03-22 00:19:30 +00:00
// connectionDeleteHandler deletes the connection configuration
func ( b * databaseBackend ) connectionDeleteHandler ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-03-22 00:19:30 +00:00
name := data . Get ( "name" ) . ( string )
if name == "" {
2017-05-01 22:30:56 +00:00
return logical . ErrorResponse ( respErrEmptyName ) , nil
2017-03-22 00:19:30 +00:00
}
2018-01-19 06:44:44 +00:00
err := req . Storage . Delete ( ctx , fmt . Sprintf ( "config/%s" , name ) )
2017-03-22 00:19:30 +00:00
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "failed to delete connection configuration: %w" , err )
2017-03-22 00:19:30 +00:00
}
2018-03-21 19:05:56 +00:00
if err := b . ClearConnection ( name ) ; err != nil {
return nil , err
2017-04-13 00:35:02 +00:00
}
2017-03-22 00:19:30 +00:00
return nil , nil
}
}
// connectionWriteHandler returns a handler function for creating and updating
// both builtin and plugin database types.
2017-04-05 23:20:31 +00:00
func ( b * databaseBackend ) connectionWriteHandler ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2018-06-19 15:24:28 +00:00
verifyConnection := data . Get ( "verify_connection" ) . ( bool )
2016-12-19 18:15:58 +00:00
2017-03-10 05:31:29 +00:00
name := data . Get ( "name" ) . ( string )
2017-03-13 21:39:55 +00:00
if name == "" {
2017-05-01 22:30:56 +00:00
return logical . ErrorResponse ( respErrEmptyName ) , nil
2017-03-13 21:39:55 +00:00
}
2018-06-19 15:24:28 +00:00
// Baseline
config := & DatabaseConfig { }
entry , err := req . Storage . Get ( ctx , fmt . Sprintf ( "config/%s" , name ) )
if err != nil {
2020-09-18 21:10:54 +00:00
return nil , fmt . Errorf ( "failed to read connection configuration: %w" , err )
2018-06-19 15:24:28 +00:00
}
if entry != nil {
if err := entry . DecodeJSON ( config ) ; err != nil {
return nil , err
}
}
if pluginNameRaw , ok := data . GetOk ( "plugin_name" ) ; ok {
config . PluginName = pluginNameRaw . ( string )
} else if req . Operation == logical . CreateOperation {
config . PluginName = data . Get ( "plugin_name" ) . ( string )
}
if config . PluginName == "" {
return logical . ErrorResponse ( respErrEmptyPluginName ) , nil
}
2022-09-09 16:32:28 +00:00
if pluginVersionRaw , ok := data . GetOk ( "plugin_version" ) ; ok {
config . PluginVersion = pluginVersionRaw . ( string )
}
2022-11-23 18:36:25 +00:00
var builtinShadowed bool
if unversionedPlugin , err := b . System ( ) . LookupPlugin ( ctx , config . PluginName , consts . PluginTypeDatabase ) ; err == nil && ! unversionedPlugin . Builtin {
builtinShadowed = true
}
2022-09-09 16:32:28 +00:00
switch {
case config . PluginVersion != "" :
semanticVersion , err := version . NewVersion ( config . PluginVersion )
if err != nil {
return logical . ErrorResponse ( "version %q is not a valid semantic version: %s" , config . PluginVersion , err ) , nil
}
// Canonicalize the version.
config . PluginVersion = "v" + semanticVersion . String ( )
2022-11-23 18:36:25 +00:00
if config . PluginVersion == versions . GetBuiltinVersion ( consts . PluginTypeDatabase , config . PluginName ) {
if builtinShadowed {
return logical . ErrorResponse ( "database plugin %q, version %s not found, as it is" +
" overridden by an unversioned plugin of the same name. Omit `plugin_version` to use the unversioned plugin" , config . PluginName , config . PluginVersion ) , nil
}
config . PluginVersion = ""
}
case builtinShadowed :
2022-09-09 16:32:28 +00:00
// We'll select the unversioned plugin that's been registered.
case req . Operation == logical . CreateOperation :
// No version provided and no unversioned plugin of that name available.
// Pin to the current latest version if any versioned plugins are registered.
plugins , err := b . System ( ) . ListVersionedPlugins ( ctx , consts . PluginTypeDatabase )
if err != nil {
return nil , err
}
var versionedCandidates [ ] pluginutil . VersionedPlugin
for _ , plugin := range plugins {
if ! plugin . Builtin && plugin . Name == config . PluginName && plugin . Version != "" {
versionedCandidates = append ( versionedCandidates , plugin )
}
}
if len ( versionedCandidates ) != 0 {
// Sort in reverse order.
sort . SliceStable ( versionedCandidates , func ( i , j int ) bool {
return versionedCandidates [ i ] . SemanticVersion . GreaterThan ( versionedCandidates [ j ] . SemanticVersion )
} )
config . PluginVersion = "v" + versionedCandidates [ 0 ] . SemanticVersion . String ( )
b . logger . Debug ( fmt . Sprintf ( "pinning %q database plugin version %q from candidates %v" , config . PluginName , config . PluginVersion , versionedCandidates ) )
}
}
2018-06-19 15:24:28 +00:00
if allowedRolesRaw , ok := data . GetOk ( "allowed_roles" ) ; ok {
config . AllowedRoles = allowedRolesRaw . ( [ ] string )
} else if req . Operation == logical . CreateOperation {
config . AllowedRoles = data . Get ( "allowed_roles" ) . ( [ ] string )
}
if rootRotationStatementsRaw , ok := data . GetOk ( "root_rotation_statements" ) ; ok {
config . RootCredentialsRotateStatements = rootRotationStatementsRaw . ( [ ] string )
} else if req . Operation == logical . CreateOperation {
config . RootCredentialsRotateStatements = data . Get ( "root_rotation_statements" ) . ( [ ] string )
}
2017-04-25 18:11:10 +00:00
2020-09-18 21:10:54 +00:00
if passwordPolicyRaw , ok := data . GetOk ( "password_policy" ) ; ok {
config . PasswordPolicy = passwordPolicyRaw . ( string )
}
2017-04-25 18:11:10 +00:00
// Remove these entries from the data before we store it keyed under
// ConnectionDetails.
delete ( data . Raw , "name" )
delete ( data . Raw , "plugin_name" )
2022-09-09 16:32:28 +00:00
delete ( data . Raw , "plugin_version" )
2017-04-25 18:11:10 +00:00
delete ( data . Raw , "allowed_roles" )
delete ( data . Raw , "verify_connection" )
2018-03-21 19:05:56 +00:00
delete ( data . Raw , "root_rotation_statements" )
2020-09-18 21:10:54 +00:00
delete ( data . Raw , "password_policy" )
2017-04-13 17:33:34 +00:00
2020-09-18 21:10:54 +00:00
id , err := uuid . GenerateUUID ( )
2017-03-21 23:05:59 +00:00
if err != nil {
2020-09-18 21:10:54 +00:00
return nil , err
2017-03-21 23:05:59 +00:00
}
2018-06-19 15:24:28 +00:00
// If this is an update, take any new values, overwrite what was there
// before, and pass that in as the "new" set of values to the plugin,
// then save what results
if req . Operation == logical . CreateOperation {
config . ConnectionDetails = data . Raw
} else {
if config . ConnectionDetails == nil {
config . ConnectionDetails = make ( map [ string ] interface { } )
}
for k , v := range data . Raw {
config . ConnectionDetails [ k ] = v
}
}
2020-09-18 21:10:54 +00:00
// Create a database plugin and initialize it.
2022-09-09 16:32:28 +00:00
dbw , err := newDatabaseWrapper ( ctx , config . PluginName , config . PluginVersion , b . System ( ) , b . logger )
2017-03-21 23:05:59 +00:00
if err != nil {
2020-09-18 21:10:54 +00:00
return logical . ErrorResponse ( "error creating database object: %s" , err ) , nil
2016-12-19 18:15:58 +00:00
}
2017-02-16 00:51:59 +00:00
2020-10-15 19:20:12 +00:00
initReq := v5 . InitializeRequest {
2020-09-18 21:10:54 +00:00
Config : config . ConnectionDetails ,
VerifyConnection : verifyConnection ,
}
initResp , err := dbw . Initialize ( ctx , initReq )
if err != nil {
dbw . Close ( )
return logical . ErrorResponse ( "error creating database object: %s" , err ) , nil
}
config . ConnectionDetails = initResp . Config
2022-02-17 14:50:33 +00:00
b . Logger ( ) . Debug ( "created database object" , "name" , name , "plugin_name" , config . PluginName )
2017-04-26 22:55:34 +00:00
// Close and remove the old connection
2022-06-17 17:05:27 +00:00
oldConn := b . connPut ( name , & dbPluginInstance {
2020-09-18 21:10:54 +00:00
database : dbw ,
2018-03-21 19:05:56 +00:00
name : name ,
id : id ,
2022-06-17 17:05:27 +00:00
} )
if oldConn != nil {
oldConn . Close ( )
2018-03-21 19:05:56 +00:00
}
2017-04-04 01:30:38 +00:00
2022-11-23 18:36:25 +00:00
// 1.12.0 and 1.12.1 stored builtin plugins in storage, but 1.12.2 reverted
// that, so clean up any pre-existing stored builtin versions on write.
if versions . IsBuiltinVersion ( config . PluginVersion ) {
config . PluginVersion = ""
}
2020-09-18 21:10:54 +00:00
err = storeConfig ( ctx , req . Storage , name , config )
2017-03-08 22:46:53 +00:00
if err != nil {
2017-03-10 05:31:29 +00:00
return nil , err
2017-03-08 22:46:53 +00:00
}
2016-12-19 18:15:58 +00:00
2017-03-10 05:31:29 +00:00
resp := & logical . Response { }
2018-04-07 15:06:04 +00:00
2022-05-17 16:21:26 +00:00
// This is a simple test to check for passwords in the connection_url parameter. If one exists,
2018-04-07 15:06:04 +00:00
// warn the user to use templated url string
if connURLRaw , ok := config . ConnectionDetails [ "connection_url" ] ; ok {
if connURL , err := url . Parse ( connURLRaw . ( string ) ) ; err == nil {
if _ , ok := connURL . User . Password ( ) ; ok {
resp . AddWarning ( "Password found in connection_url, use a templated url to enable root rotation and prevent read access to password information." )
}
}
}
2016-12-19 18:15:58 +00:00
2020-09-18 21:10:54 +00:00
// If using a legacy DB plugin and set the `password_policy` field, send a warning to the user indicating
// the `password_policy` will not be used
if dbw . isV4 ( ) && config . PasswordPolicy != "" {
resp . AddWarning ( fmt . Sprintf ( "%s does not support password policies - upgrade to the latest version of " +
"Vault (or the sdk if using a custom plugin) to gain password policy support" , config . PluginName ) )
}
2022-02-17 14:50:33 +00:00
if len ( resp . Warnings ) == 0 {
return nil , nil
}
2017-03-10 05:31:29 +00:00
return resp , nil
}
2016-12-19 18:15:58 +00:00
}
2020-09-18 21:10:54 +00:00
func storeConfig ( ctx context . Context , storage logical . Storage , name string , config * DatabaseConfig ) error {
entry , err := logical . StorageEntryJSON ( fmt . Sprintf ( "config/%s" , name ) , config )
if err != nil {
return fmt . Errorf ( "unable to marshal object to JSON: %w" , err )
}
err = storage . Put ( ctx , entry )
if err != nil {
return fmt . Errorf ( "failed to save object: %w" , err )
}
return nil
}
2016-12-19 18:15:58 +00:00
const pathConfigConnectionHelpSyn = `
2017-04-11 18:50:34 +00:00
Configure connection details to a database plugin .
2016-12-19 18:15:58 +00:00
`
const pathConfigConnectionHelpDesc = `
2017-04-11 18:50:34 +00:00
This path configures the connection details used to connect to a particular
database . This path runs the provided plugin name and passes the configured
connection details to the plugin . See the documentation for the plugin specified
for a full list of accepted connection details .
2016-12-19 18:15:58 +00:00
2017-05-04 00:37:34 +00:00
In addition to the database specific connection details , this endpoint also
2017-04-11 18:50:34 +00:00
accepts :
* "plugin_name" ( required ) - The name of a builtin or previously registered
plugin known to vault . This endpoint will create an instance of that
plugin type .
2017-04-13 00:35:02 +00:00
* "verify_connection" ( default : true ) - A boolean value denoting if the plugin should verify
2017-04-11 18:50:34 +00:00
it is able to connect to the database using the provided connection
details .
`
const pathResetConnectionHelpSyn = `
Resets a database plugin .
`
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
const pathResetConnectionHelpDesc = `
This path resets the database connection by closing the existing database plugin
instance and running a new one .
2016-12-19 18:15:58 +00:00
`