Add support for passing args via vault read (#5093)

We support this in the API as of 0.10.2 so read should support it too.

Trivially tested with some log info:

`core: data: data="map[string]interface {}{"zip":[]string{"zap", "zap2"}}"`
This commit is contained in:
Jeff Mitchell
2018-08-13 22:00:26 -04:00
committed by GitHub
parent 635a0e5b1a
commit f562a71808
4 changed files with 100 additions and 48 deletions

View File

@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"net/url"
"os"
"github.com/hashicorp/errwrap"
@@ -46,8 +47,26 @@ func (c *Client) Logical() *Logical {
}
func (c *Logical) Read(path string) (*Secret, error) {
return c.ReadWithData(path, nil)
}
func (c *Logical) ReadWithData(path string, data map[string][]string) (*Secret, error) {
r := c.c.NewRequest("GET", "/v1/"+path)
var values url.Values
for k, v := range data {
if values == nil {
values = make(url.Values)
}
for _, val := range v {
values.Add(k, val)
}
}
if values != nil {
r.Params = values
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)

View File

@@ -152,6 +152,22 @@ func parseArgsDataString(stdin io.Reader, args []string) (map[string]string, err
return result, nil
}
// parseArgsDataStringLists parses the args data and returns the values as
// string lists. If the values cannot be represented as strings, an error is
// returned.
func parseArgsDataStringLists(stdin io.Reader, args []string) (map[string][]string, error) {
raw, err := parseArgsData(stdin, args)
if err != nil {
return nil, err
}
var result map[string][]string
if err := mapstructure.WeakDecode(raw, &result); err != nil {
return nil, errors.Wrap(err, "failed to convert values to strings")
}
return result, nil
}
// truncateToSeconds truncates the given duration to the number of seconds. If
// the duration is less than 1s, it is returned as 0. The integer represents
// the whole number unit of seconds for the duration.

View File

@@ -2,6 +2,8 @@ package command
import (
"fmt"
"io"
"os"
"strings"
"github.com/mitchellh/cli"
@@ -13,6 +15,8 @@ var _ cli.CommandAutocomplete = (*ReadCommand)(nil)
type ReadCommand struct {
*BaseCommand
testStdin io.Reader // for tests
}
func (c *ReadCommand) Synopsis() string {
@@ -63,9 +67,6 @@ func (c *ReadCommand) Run(args []string) int {
case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args)))
return 1
case len(args) > 1:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
return 1
}
client, err := c.Client()
@@ -74,9 +75,21 @@ func (c *ReadCommand) Run(args []string) int {
return 2
}
// Pull our fake stdin if needed
stdin := (io.Reader)(os.Stdin)
if c.testStdin != nil {
stdin = c.testStdin
}
path := sanitizePath(args[0])
secret, err := client.Logical().Read(path)
data, err := parseArgsDataStringLists(stdin, args[1:])
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
return 1
}
secret, err := client.Logical().ReadWithData(path, data)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading %s: %s", path, err))
return 2

View File

@@ -28,75 +28,79 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques
return nil, http.StatusNotFound, nil
}
var data map[string]interface{}
// Determine the operation
var op logical.Operation
switch r.Method {
case "DELETE":
op = logical.DeleteOperation
case "GET":
op = logical.ReadOperation
// Need to call ParseForm to get query params loaded
queryVals := r.URL.Query()
var list bool
var err error
listStr := queryVals.Get("list")
if listStr != "" {
list, err := strconv.ParseBool(listStr)
list, err = strconv.ParseBool(listStr)
if err != nil {
return nil, http.StatusBadRequest, nil
}
if list {
op = logical.ListOperation
if !strings.HasSuffix(path, "/") {
path += "/"
}
}
}
if !list {
getData := map[string]interface{}{}
for k, v := range r.URL.Query() {
// Skip the help key as this is a reserved parameter
if k == "help" {
continue
}
switch {
case len(v) == 0:
case len(v) == 1:
getData[k] = v[0]
default:
getData[k] = v
}
}
if len(getData) > 0 {
data = getData
}
}
case "POST", "PUT":
op = logical.UpdateOperation
// Parse the request if we can
if op == logical.UpdateOperation {
err := parseRequest(r, w, &data)
if err == io.EOF {
data = nil
err = nil
}
if err != nil {
return nil, http.StatusBadRequest, err
}
}
case "LIST":
op = logical.ListOperation
case "OPTIONS":
default:
return nil, http.StatusMethodNotAllowed, nil
}
if op == logical.ListOperation {
if !strings.HasSuffix(path, "/") {
path += "/"
}
}
// Parse the request if we can
var data map[string]interface{}
if op == logical.UpdateOperation {
err := parseRequest(r, w, &data)
if err == io.EOF {
data = nil
err = nil
}
if err != nil {
return nil, http.StatusBadRequest, err
}
}
// If we are a read operation, try and parse any parameters
if op == logical.ReadOperation {
getData := map[string]interface{}{}
for k, v := range r.URL.Query() {
// Skip the help key as this is a reserved parameter
if k == "help" {
continue
}
switch {
case len(v) == 0:
case len(v) == 1:
getData[k] = v[0]
default:
getData[k] = v
}
}
if len(getData) > 0 {
data = getData
}
case "OPTIONS":
default:
return nil, http.StatusMethodNotAllowed, nil
}
var err error