2020-10-10 16:12:04 -04:00
package connect
import (
2021-06-25 14:06:31 -04:00
"fmt"
"io/ioutil"
"os"
2020-10-10 16:12:04 -04:00
"strings"
"github.com/hashicorp/boundary/internal/cmd/base"
2021-06-30 18:02:03 -04:00
"github.com/mitchellh/mapstructure"
2020-10-10 16:12:04 -04:00
"github.com/posener/complete"
)
const (
postgresSynopsis = "Authorize a session against a target and invoke a Postgres client to connect"
)
func postgresOptions ( c * Command , set * base . FlagSets ) {
f := set . NewFlagSet ( "Postgres Options" )
f . StringVar ( & base . StringVar {
Name : "style" ,
Target : & c . flagPostgresStyle ,
EnvVar : "BOUNDARY_CONNECT_POSTGRES_STYLE" ,
Completion : complete . PredictSet ( "psql" ),
Default : "psql" ,
Usage : `Specifies how the CLI will attempt to invoke a Postgres client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "psql".` ,
})
f . StringVar ( & base . StringVar {
Name : "username" ,
Target : & c . flagUsername ,
EnvVar : "BOUNDARY_CONNECT_USERNAME" ,
Completion : complete . PredictNothing ,
2021-06-25 14:06:31 -04:00
Usage : `Specifies the username to pass through to the client. May be overridden by credentials sourced from a credential store.` ,
})
f . StringVar ( & base . StringVar {
Name : "dbname" ,
Target : & c . flagDbname ,
EnvVar : "BOUNDARY_CONNECT_DBNAME" ,
Completion : complete . PredictNothing ,
Usage : `Specifies the database name to pass through to the client.` ,
2020-10-10 16:12:04 -04:00
})
}
type postgresFlags struct {
flagPostgresStyle string
}
func ( p * postgresFlags ) defaultExec () string {
return strings . ToLower ( p . flagPostgresStyle )
}
2021-06-25 14:06:31 -04:00
type postgresCredentials struct {
2021-06-30 18:02:03 -04:00
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
2021-06-25 14:06:31 -04:00
}
func ( p * postgresFlags ) buildArgs ( c * Command , port , ip , addr string ) ( args , envs [] string , retErr error ) {
var creds postgresCredentials
if c . sessionAuthz != nil && len ( c . sessionAuthz . Credentials ) > 0 {
for _ , cred := range c . sessionAuthz . Credentials {
2021-06-30 18:02:03 -04:00
if cred . Secret == nil || cred . Secret . Decoded == nil {
continue
}
2021-06-25 14:06:31 -04:00
// TODO: Could allow switching on library ID or name
switch cred . CredentialLibrary . Type {
case "vault" :
// Attempt unmarshaling into creds
2021-06-30 18:02:03 -04:00
if err := mapstructure . Decode ( cred . Secret . Decoded , & creds ); err != nil {
return nil , nil , fmt . Errorf ( "Error interpreting Vault secret: %w" , err )
2021-06-25 14:06:31 -04:00
}
}
if creds . Username != "" && creds . Password != "" {
// In the future we can look for other types if we support other
// authentication mechanisms
break
}
}
}
2020-10-10 16:12:04 -04:00
switch p . flagPostgresStyle {
case "psql" :
args = append ( args , "-p" , port , "-h" , ip )
2021-06-25 14:06:31 -04:00
if c . flagDbname != "" {
args = append ( args , "-d" , c . flagDbname )
}
switch {
case creds . Username != "" :
args = append ( args , "-U" , creds . Username )
case c . flagUsername != "" :
2020-10-10 16:12:04 -04:00
args = append ( args , "-U" , c . flagUsername )
}
2021-06-25 14:06:31 -04:00
if creds . Password != "" {
passfile , err := ioutil . TempFile ( "" , "*" )
if err != nil {
return nil , nil , fmt . Errorf ( "Error saving postgres password to tmp file: %w" , err )
}
c . cleanupFuncs = append ( c . cleanupFuncs , func () error {
if err := os . Remove ( passfile . Name ()); err != nil {
return fmt . Errorf ( "Error removing temporary password file; consider removing %s manually: %w" , passfile . Name (), err )
}
return nil
})
_ , err = passfile . WriteString ( fmt . Sprintf ( "*:*:*:*:%s" , creds . Password ))
if err != nil {
return nil , nil , fmt . Errorf ( "Error writing password file to %s: %w" , passfile . Name (), err )
}
if err := passfile . Close (); err != nil {
return nil , nil , fmt . Errorf ( "Error closing password file after writing to %s: %w" , passfile . Name (), err )
}
envs = append ( envs , fmt . Sprintf ( "PGPASSFILE=%s" , passfile . Name ()))
if c . flagDbname == "" {
c . UI . Warn ( "Credentials are being brokered but no -dbname parameter provided. psql may misinterpret another parameter as the database name." )
}
}
2020-10-10 16:12:04 -04:00
}
2021-06-25 14:06:31 -04:00
return
2020-10-10 16:12:04 -04:00
}