Add response wrapping to list operations (#1814)
This commit is contained in:
parent
90737d3b44
commit
1c6f2fd82b
|
@ -38,7 +38,10 @@ func (c *Logical) Read(path string) (*Secret, error) {
|
|||
}
|
||||
|
||||
func (c *Logical) List(path string) (*Secret, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/"+path)
|
||||
r := c.c.NewRequest("LIST", "/v1/"+path)
|
||||
// Set this for broader compatibility, but we use LIST above to be able to
|
||||
// handle the wrapping lookup function
|
||||
r.Method = "GET"
|
||||
r.Params.Set("list", "true")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if resp != nil {
|
||||
|
|
|
@ -60,6 +60,10 @@ func (c *ListCommand) Run(args []string) int {
|
|||
"No value found at %s", path))
|
||||
return 1
|
||||
}
|
||||
if secret.WrapInfo != nil && secret.WrapInfo.TTL != 0 {
|
||||
return OutputSecret(c.Ui, format, secret)
|
||||
}
|
||||
|
||||
if secret.Data["keys"] == nil {
|
||||
c.Ui.Error("No entries found")
|
||||
return 0
|
||||
|
|
|
@ -67,6 +67,15 @@ func (c *UnwrapCommand) Run(args []string) int {
|
|||
return PrintRawField(c.Ui, secret, field)
|
||||
}
|
||||
|
||||
// Check if the original was a list response and format as a list if so
|
||||
if secret.Data != nil &&
|
||||
len(secret.Data) == 1 &&
|
||||
secret.Data["keys"] != nil {
|
||||
_, ok := secret.Data["keys"].([]interface{})
|
||||
if ok {
|
||||
return OutputList(c.Ui, format, secret)
|
||||
}
|
||||
}
|
||||
return OutputSecret(c.Ui, format, secret)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/http"
|
||||
|
@ -40,6 +41,9 @@ func TestUnwrap(t *testing.T) {
|
|||
if method == "GET" && path == "secret/foo" {
|
||||
return "60s"
|
||||
}
|
||||
if method == "LIST" && path == "secret" {
|
||||
return "60s"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
client.SetWrappingLookupFunc(wrapLookupFunc)
|
||||
|
@ -71,4 +75,33 @@ func TestUnwrap(t *testing.T) {
|
|||
if output != "zap\n" {
|
||||
t.Fatalf("unexpectd output:\n%s", output)
|
||||
}
|
||||
|
||||
// Now test with list handling, specifically that it will be called with
|
||||
// the list output formatter
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
outer, err = client.Logical().List("secret")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if outer == nil {
|
||||
t.Fatal("outer response was nil")
|
||||
}
|
||||
if outer.WrapInfo == nil {
|
||||
t.Fatal("outer wrapinfo was nil, response was %#v", *outer)
|
||||
}
|
||||
|
||||
args = []string{
|
||||
"-address", addr,
|
||||
outer.WrapInfo.Token,
|
||||
}
|
||||
// Run the read
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
output = ui.OutputWriter.String()
|
||||
if strings.TrimSpace(output) != "Keys\n----\nfoo" {
|
||||
t.Fatalf("unexpected output:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,17 +120,17 @@ func handleLogical(core *vault.Core, dataOnly bool, prepareRequestCallback Prepa
|
|||
|
||||
// Basically: if we have empty "keys" or no keys at all, 404. This
|
||||
// provides consistency with GET.
|
||||
case req.Operation == logical.ListOperation:
|
||||
case req.Operation == logical.ListOperation && resp.WrapInfo == nil:
|
||||
if resp == nil || len(resp.Data) == 0 {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
keysInt, ok := resp.Data["keys"]
|
||||
if !ok || keysInt == nil {
|
||||
keysRaw, ok := resp.Data["keys"]
|
||||
if !ok || keysRaw == nil {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
keys, ok := keysInt.([]string)
|
||||
keys, ok := keysRaw.([]string)
|
||||
if !ok {
|
||||
respondError(w, http.StatusInternalServerError, nil)
|
||||
return
|
||||
|
|
|
@ -357,6 +357,25 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, *log
|
|||
}
|
||||
|
||||
func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*logical.Response, error) {
|
||||
// Before wrapping, obey special rules for listing: if no entries are
|
||||
// found, 404. This prevents unwrapping only to find empty data.
|
||||
if req.Operation == logical.ListOperation {
|
||||
if resp == nil || len(resp.Data) == 0 {
|
||||
return nil, logical.ErrUnsupportedPath
|
||||
}
|
||||
keysRaw, ok := resp.Data["keys"]
|
||||
if !ok || keysRaw == nil {
|
||||
return nil, logical.ErrUnsupportedPath
|
||||
}
|
||||
keys, ok := keysRaw.([]string)
|
||||
if !ok {
|
||||
return nil, logical.ErrUnsupportedPath
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil, logical.ErrUnsupportedPath
|
||||
}
|
||||
}
|
||||
|
||||
// If we are wrapping, the first part (performed in this functions) happens
|
||||
// before auditing so that resp.WrapInfo.Token can contain the HMAC'd
|
||||
// wrapping token ID in the audit logs, so that it can be determined from
|
||||
|
|
Loading…
Reference in a new issue