diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fb0e5ad9..94f9db735 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ IMPROVEMENTS: different paths using the command options [GH-1532] * cli: `vault auth -methods` will now display the config settings of the mount [GH-1531] + * cli: `vault read/write/unwrap -field` now allows selecting token response + fields [GH-1567] + * cli: `vault write -field` now allows selecting wrapped response fields + [GH-1567] * credential/aws-ec2: Added a new constraint, 'bound_account_id' to the role [GH-1523] * secret/aws: Listing of roles is supported now [GH-1546] @@ -38,6 +42,8 @@ BUG FIXES: during renewal [GH-1542] * core: Fix regression causing status codes to be `400` in most non-5xx error cases [GH-1553] + * physical/postgres: Remove use of prepared statements as this causes + connection multiplexing software to break [GH-1548] ## 0.6.0 (June 14th, 2016) diff --git a/command/server_test.go b/command/server_test.go index 583475088..17eb92c7c 100644 --- a/command/server_test.go +++ b/command/server_test.go @@ -230,7 +230,7 @@ func TestServer_ReloadListener(t *testing.T) { } checkFinished() - time.Sleep(2 * time.Second) + time.Sleep(5 * time.Second) checkFinished() if err := testCertificateName("foo.example.com"); err != nil { diff --git a/command/util.go b/command/util.go index fc10487d3..6b50aa6a0 100644 --- a/command/util.go +++ b/command/util.go @@ -31,27 +31,44 @@ func DefaultTokenHelper() (token.TokenHelper, error) { func PrintRawField(ui cli.Ui, secret *api.Secret, field string) int { var val interface{} - switch field { - case "wrapping_token": - if secret.WrapInfo != nil { + switch { + case secret.Auth != nil: + switch field { + case "token": + val = secret.Auth.ClientToken + case "token_accessor": + val = secret.Auth.Accessor + case "token_duration": + val = secret.Auth.LeaseDuration + case "token_renewable": + val = secret.Auth.Renewable + case "token_policies": + val = secret.Auth.Policies + default: + val = secret.Data[field] + } + + case secret.WrapInfo != nil: + switch field { + case "wrapping_token": val = secret.WrapInfo.Token - } - case "wrapping_token_ttl": - if secret.WrapInfo != nil { + case "wrapping_token_ttl": val = secret.WrapInfo.TTL - } - case "wrapping_token_creation_time": - if secret.WrapInfo != nil { + case "wrapping_token_creation_time": val = secret.WrapInfo.CreationTime.String() - } - case "wrapped_accessor": - if secret.WrapInfo != nil { + case "wrapped_accessor": val = secret.WrapInfo.WrappedAccessor + default: + val = secret.Data[field] } - case "refresh_interval": - val = secret.LeaseDuration + default: - val = secret.Data[field] + switch field { + case "refresh_interval": + val = secret.LeaseDuration + default: + val = secret.Data[field] + } } if val != nil { diff --git a/command/write.go b/command/write.go index 0342f5dbb..cf67a8b66 100644 --- a/command/write.go +++ b/command/write.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "os" - "reflect" "strings" "github.com/hashicorp/vault/helper/kv-builder" @@ -75,23 +74,7 @@ func (c *WriteCommand) Run(args []string) int { // Handle single field output if field != "" { - if val, ok := secret.Data[field]; ok { - // c.Ui.Output() prints a CR character which in this case is - // not desired. Since Vault CLI currently only uses BasicUi, - // which writes to standard output, os.Stdout is used here to - // directly print the message. If mitchellh/cli exposes method - // to print without CR, this check needs to be removed. - if reflect.TypeOf(c.Ui).String() == "*cli.BasicUi" { - fmt.Fprintf(os.Stdout, fmt.Sprintf("%v", val)) - } else { - c.Ui.Output(fmt.Sprintf("%v", val)) - } - return 0 - } else { - c.Ui.Error(fmt.Sprintf( - "Field %s not present in secret", field)) - return 1 - } + return PrintRawField(c.Ui, secret, field) } return OutputSecret(c.Ui, format, secret) diff --git a/physical/postgresql.go b/physical/postgresql.go index 3d22f8fc3..2fe2b4faf 100644 --- a/physical/postgresql.go +++ b/physical/postgresql.go @@ -14,9 +14,12 @@ import ( // PostgreSQL Backend is a physical backend that stores data // within a PostgreSQL database. type PostgreSQLBackend struct { - table string - client *sql.DB - statements map[string]*sql.Stmt + table string + client *sql.DB + put_query string + get_query string + delete_query string + list_query string logger *log.Logger } @@ -50,49 +53,30 @@ func newPostgreSQLBackend(conf map[string]string, logger *log.Logger) (Backend, // Setup our put strategy based on the presence or absence of a native // upsert. - var put_statement string + var put_query string if upsert_required { - put_statement = "SELECT vault_kv_put($1, $2, $3, $4)" + put_query = "SELECT vault_kv_put($1, $2, $3, $4)" } else { - put_statement = "INSERT INTO " + quoted_table + " VALUES($1, $2, $3, $4)" + + put_query = "INSERT INTO " + quoted_table + " VALUES($1, $2, $3, $4)" + " ON CONFLICT (path, key) DO " + " UPDATE SET (parent_path, path, key, value) = ($1, $2, $3, $4)" } // Setup the backend. m := &PostgreSQLBackend{ - table: quoted_table, - client: db, - statements: make(map[string]*sql.Stmt), + table: quoted_table, + client: db, + put_query: put_query, + get_query: "SELECT value FROM " + quoted_table + " WHERE path = $1 AND key = $2", + delete_query: "DELETE FROM " + quoted_table + " WHERE path = $1 AND key = $2", + list_query: "SELECT key FROM " + quoted_table + " WHERE path = $1" + + "UNION SELECT substr(path, length($1)+1) FROM " + quoted_table + "WHERE parent_path = $1", logger: logger, } - // Prepare all the statements required - statements := map[string]string{ - "put": put_statement, - "get": "SELECT value FROM " + quoted_table + " WHERE path = $1 AND key = $2", - "delete": "DELETE FROM " + quoted_table + " WHERE path = $1 AND key = $2", - "list": "SELECT key FROM " + quoted_table + " WHERE path = $1" + - "UNION SELECT substr(path, length($1)+1) FROM " + quoted_table + "WHERE parent_path = $1", - } - for name, query := range statements { - if err := m.prepare(name, query); err != nil { - return nil, err - } - } return m, nil } -// prepare is a helper to prepare a query for future execution -func (m *PostgreSQLBackend) prepare(name, query string) error { - stmt, err := m.client.Prepare(query) - if err != nil { - return fmt.Errorf("failed to prepare '%s': %v", name, err) - } - m.statements[name] = stmt - return nil -} - // splitKey is a helper to split a full path key into individual // parts: parentPath, path, key func (m *PostgreSQLBackend) splitKey(fullPath string) (string, string, string) { @@ -123,7 +107,7 @@ func (m *PostgreSQLBackend) Put(entry *Entry) error { parentPath, path, key := m.splitKey(entry.Key) - _, err := m.statements["put"].Exec(parentPath, path, key, entry.Value) + _, err := m.client.Exec(m.put_query, parentPath, path, key, entry.Value) if err != nil { return err } @@ -137,7 +121,7 @@ func (m *PostgreSQLBackend) Get(fullPath string) (*Entry, error) { _, path, key := m.splitKey(fullPath) var result []byte - err := m.statements["get"].QueryRow(path, key).Scan(&result) + err := m.client.QueryRow(m.get_query, path, key).Scan(&result) if err == sql.ErrNoRows { return nil, nil } @@ -158,7 +142,7 @@ func (m *PostgreSQLBackend) Delete(fullPath string) error { _, path, key := m.splitKey(fullPath) - _, err := m.statements["delete"].Exec(path, key) + _, err := m.client.Exec(m.delete_query, path, key) if err != nil { return err } @@ -170,7 +154,7 @@ func (m *PostgreSQLBackend) Delete(fullPath string) error { func (m *PostgreSQLBackend) List(prefix string) ([]string, error) { defer metrics.MeasureSince([]string{"postgres", "list"}, time.Now()) - rows, err := m.statements["list"].Query("/" + prefix) + rows, err := m.client.Query(m.list_query, "/" + prefix) if err != nil { return nil, err } diff --git a/physical/postgresql_test.go b/physical/postgresql_test.go index 92b014ec2..026ed2961 100644 --- a/physical/postgresql_test.go +++ b/physical/postgresql_test.go @@ -32,7 +32,7 @@ func TestPostgreSQLBackend(t *testing.T) { defer func() { pg := b.(*PostgreSQLBackend) - _, err := pg.client.Exec("DROP TABLE " + pg.table) + _, err := pg.client.Exec("TRUNCATE TABLE " + pg.table) if err != nil { t.Fatalf("Failed to drop table: %v", err) } diff --git a/vault/token_store.go b/vault/token_store.go index 69af8d3f2..685285a92 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "regexp" - "strconv" "strings" "sync" "time" @@ -92,8 +91,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) t.tokenLocks = map[string]*sync.RWMutex{} for i := int64(0); i < 256; i++ { - t.tokenLocks[fmt.Sprintf("%2x", - strconv.FormatInt(i, 16))] = &sync.RWMutex{} + t.tokenLocks[fmt.Sprintf("%02x", i)] = &sync.RWMutex{} } t.tokenLocks["custom"] = &sync.RWMutex{}