diff --git a/api/logical.go b/api/logical.go index d212066d86dd..d5f946446b62 100644 --- a/api/logical.go +++ b/api/logical.go @@ -161,8 +161,26 @@ func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, erro } func (c *Logical) Delete(path string) (*Secret, error) { + return c.DeleteWithData(path, nil) +} + +func (c *Logical) DeleteWithData(path string, data map[string][]string) (*Secret, error) { r := c.c.NewRequest("DELETE", "/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) diff --git a/command/delete.go b/command/delete.go index 221c5b72ed24..75bbf0aabb50 100644 --- a/command/delete.go +++ b/command/delete.go @@ -2,6 +2,8 @@ package command import ( "fmt" + "io" + "os" "strings" "github.com/mitchellh/cli" @@ -13,6 +15,8 @@ var _ cli.CommandAutocomplete = (*DeleteCommand)(nil) type DeleteCommand struct { *BaseCommand + + testStdin io.Reader // for tests } func (c *DeleteCommand) Synopsis() string { @@ -69,10 +73,7 @@ func (c *DeleteCommand) Run(args []string) int { args = f.Args() switch { 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))) + c.UI.Error(fmt.Sprintf("Not enough arguments (expected at least 1, got %d)", len(args))) return 1 } @@ -82,9 +83,21 @@ func (c *DeleteCommand) 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().Delete(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().DeleteWithData(path, data) if err != nil { c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err)) if secret != nil { diff --git a/command/delete_test.go b/command/delete_test.go index 2d642b1fc9b2..e26d393b16fe 100644 --- a/command/delete_test.go +++ b/command/delete_test.go @@ -27,18 +27,24 @@ func TestDeleteCommand_Run(t *testing.T) { out string code int }{ + { + "default", + []string{"secret/foo"}, + "", + 0, + }, + { + "optional_args", + []string{"secret/foo", "bar=baz"}, + "", + 0, + }, { "not_enough_args", []string{}, "Not enough arguments", 1, }, - { - "too_many_args", - []string{"foo", "bar"}, - "Too many arguments", - 1, - }, } t.Run("validations", func(t *testing.T) { diff --git a/http/handler.go b/http/handler.go index 6d10a5bc113a..2562c6a052c2 100644 --- a/http/handler.go +++ b/http/handler.go @@ -455,6 +455,29 @@ func (fs *UIAssetWrapper) Open(name string) (http.File, error) { return nil, err } +func parseQuery(values url.Values) map[string]interface{} { + data := map[string]interface{}{} + for k, v := range values { + // Skip the help key as this is a reserved parameter + if k == "help" { + continue + } + + switch { + case len(v) == 0: + case len(v) == 1: + data[k] = v[0] + default: + data[k] = v + } + } + + if len(data) > 0 { + return data + } + return nil +} + func parseRequest(core *vault.Core, r *http.Request, w http.ResponseWriter, out interface{}) (io.ReadCloser, error) { // Limit the maximum number of bytes to MaxRequestSize to protect // against an indefinite amount of data being read. diff --git a/http/logical.go b/http/logical.go index b9481bc61f67..4147693ea81c 100644 --- a/http/logical.go +++ b/http/logical.go @@ -35,7 +35,7 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques switch r.Method { case "DELETE": op = logical.DeleteOperation - + data = parseQuery(r.URL.Query()) case "GET": op = logical.ReadOperation queryVals := r.URL.Query() @@ -56,27 +56,9 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques } 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 - } + data = parseQuery(queryVals) } + if path == "sys/storage/raft/snapshot" { responseWriter = w }