mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
Add list capability, which will work with the generic and cubbyhole
backends for the moment. This is pretty simple; it just adds the actual capability to make a list call into both the CLI and the HTTP handler. The real meat was already in those backends.
This commit is contained in:
@@ -26,6 +26,20 @@ func (c *Logical) Read(path string) (*Secret, error) {
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) List(path string) (*Secret, error) {
|
||||
r := c.c.NewRequest("LIST", "/v1/"+path)
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/"+path)
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
|
||||
@@ -161,6 +161,7 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||
"read": func() (cli.Command, error) {
|
||||
return &command.ReadCommand{
|
||||
Meta: meta,
|
||||
List: false,
|
||||
}, nil
|
||||
},
|
||||
|
||||
@@ -176,6 +177,13 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||
}, nil
|
||||
},
|
||||
|
||||
"list": func() (cli.Command, error) {
|
||||
return &command.ReadCommand{
|
||||
Meta: meta,
|
||||
List: true,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"rekey": func() (cli.Command, error) {
|
||||
return &command.RekeyCommand{
|
||||
Meta: meta,
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
// ReadCommand is a Command that reads data from the Vault.
|
||||
type ReadCommand struct {
|
||||
Meta
|
||||
List bool
|
||||
}
|
||||
|
||||
func (c *ReadCommand) Run(args []string) int {
|
||||
var format string
|
||||
var field string
|
||||
flags := c.Meta.FlagSet("read", FlagSetDefault)
|
||||
var err error
|
||||
var secret *api.Secret
|
||||
var flags *flag.FlagSet
|
||||
if c.List {
|
||||
flags = c.Meta.FlagSet("list", FlagSetDefault)
|
||||
} else {
|
||||
flags = c.Meta.FlagSet("read", FlagSetDefault)
|
||||
}
|
||||
flags.StringVar(&format, "format", "table", "")
|
||||
flags.StringVar(&field, "field", "", "")
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
@@ -42,7 +53,11 @@ func (c *ReadCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
secret, err := client.Logical().Read(path)
|
||||
if c.List {
|
||||
secret, err = client.Logical().List(path)
|
||||
} else {
|
||||
secret, err = client.Logical().Read(path)
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error reading %s: %s", path, err))
|
||||
@@ -79,6 +94,9 @@ func (c *ReadCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
func (c *ReadCommand) Synopsis() string {
|
||||
if c.List {
|
||||
return "List data in Vault"
|
||||
}
|
||||
return "Read data or secrets from Vault"
|
||||
}
|
||||
|
||||
@@ -88,11 +106,25 @@ Usage: vault read [options] path
|
||||
|
||||
Read data from Vault.
|
||||
|
||||
Read reads data at the given path from Vault. This can be used to
|
||||
read secrets and configuration as well as generate dynamic values from
|
||||
Reads data at the given path from Vault. This can be used to read
|
||||
secrets and configuration as well as generate dynamic values from
|
||||
materialized backends. Please reference the documentation for the
|
||||
backends in use to determine key structure.
|
||||
`
|
||||
|
||||
if c.List {
|
||||
helpText =
|
||||
`
|
||||
Usage: vault list [options] path
|
||||
|
||||
List data from Vault.
|
||||
|
||||
Retrieve a listing of available data. The data returned is
|
||||
backend-specific, and not all backends implement listing capability.
|
||||
`
|
||||
}
|
||||
|
||||
helpText += `
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage() + `
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/http"
|
||||
@@ -134,3 +135,64 @@ func TestRead_field_notFound(t *testing.T) {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &ReadCommand{
|
||||
Meta: Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
},
|
||||
List: true,
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"-format", "json",
|
||||
"secret",
|
||||
}
|
||||
|
||||
// Run once so the client is setup, ignore errors
|
||||
c.Run(args)
|
||||
|
||||
// Get the client so we can write data
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
data := map[string]interface{}{"value": "bar"}
|
||||
if _, err := client.Logical().Write("secret/foo", data); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
data = map[string]interface{}{"value": "bar"}
|
||||
if _, err := client.Logical().Write("secret/foo/bar", data); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
secret, err := client.Logical().List("secret/")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if secret == nil {
|
||||
t.Fatalf("err: No value found at secret/")
|
||||
}
|
||||
|
||||
if secret.Data == nil {
|
||||
t.Fatalf("err: Data not found")
|
||||
}
|
||||
|
||||
exp := map[string]interface{}{
|
||||
"keys": []interface{}{"foo", "foo/"},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(secret.Data, exp) {
|
||||
t.Fatalf("err: expected %#v, got %#v", exp, secret.Data)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user