mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
Add response wrapping to list operations (#1814)
This commit is contained in:
@@ -38,7 +38,10 @@ func (c *Logical) Read(path string) (*Secret, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Logical) List(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")
|
r.Params.Set("list", "true")
|
||||||
resp, err := c.c.RawRequest(r)
|
resp, err := c.c.RawRequest(r)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ func (c *ListCommand) Run(args []string) int {
|
|||||||
"No value found at %s", path))
|
"No value found at %s", path))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
if secret.WrapInfo != nil && secret.WrapInfo.TTL != 0 {
|
||||||
|
return OutputSecret(c.Ui, format, secret)
|
||||||
|
}
|
||||||
|
|
||||||
if secret.Data["keys"] == nil {
|
if secret.Data["keys"] == nil {
|
||||||
c.Ui.Error("No entries found")
|
c.Ui.Error("No entries found")
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -67,6 +67,15 @@ func (c *UnwrapCommand) Run(args []string) int {
|
|||||||
return PrintRawField(c.Ui, secret, field)
|
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)
|
return OutputSecret(c.Ui, format, secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/http"
|
"github.com/hashicorp/vault/http"
|
||||||
@@ -40,6 +41,9 @@ func TestUnwrap(t *testing.T) {
|
|||||||
if method == "GET" && path == "secret/foo" {
|
if method == "GET" && path == "secret/foo" {
|
||||||
return "60s"
|
return "60s"
|
||||||
}
|
}
|
||||||
|
if method == "LIST" && path == "secret" {
|
||||||
|
return "60s"
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
client.SetWrappingLookupFunc(wrapLookupFunc)
|
client.SetWrappingLookupFunc(wrapLookupFunc)
|
||||||
@@ -71,4 +75,33 @@ func TestUnwrap(t *testing.T) {
|
|||||||
if output != "zap\n" {
|
if output != "zap\n" {
|
||||||
t.Fatalf("unexpectd output:\n%s", output)
|
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
|
// Basically: if we have empty "keys" or no keys at all, 404. This
|
||||||
// provides consistency with GET.
|
// provides consistency with GET.
|
||||||
case req.Operation == logical.ListOperation:
|
case req.Operation == logical.ListOperation && resp.WrapInfo == nil:
|
||||||
if resp == nil || len(resp.Data) == 0 {
|
if resp == nil || len(resp.Data) == 0 {
|
||||||
respondError(w, http.StatusNotFound, nil)
|
respondError(w, http.StatusNotFound, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keysInt, ok := resp.Data["keys"]
|
keysRaw, ok := resp.Data["keys"]
|
||||||
if !ok || keysInt == nil {
|
if !ok || keysRaw == nil {
|
||||||
respondError(w, http.StatusNotFound, nil)
|
respondError(w, http.StatusNotFound, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keys, ok := keysInt.([]string)
|
keys, ok := keysRaw.([]string)
|
||||||
if !ok {
|
if !ok {
|
||||||
respondError(w, http.StatusInternalServerError, nil)
|
respondError(w, http.StatusInternalServerError, nil)
|
||||||
return
|
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) {
|
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
|
// If we are wrapping, the first part (performed in this functions) happens
|
||||||
// before auditing so that resp.WrapInfo.Token can contain the HMAC'd
|
// 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
|
// wrapping token ID in the audit logs, so that it can be determined from
|
||||||
|
|||||||
Reference in New Issue
Block a user