Output full secret path in certain kv commands (#14301)

* Full secret path in table output of get and put

* Add path output to KV patch and metadata get

* Add changelog

* Don't print secret path for kv-v1

* Make more readable

* Switch around logic to not swallow error

* Add test for secret path

* Fix metadata test

* Add unit test for padequalsigns

* Remove wonky kv get tests
This commit is contained in:
VAL 2022-03-08 13:17:27 -08:00 committed by GitHub
parent b9a6ec67c9
commit 63a2ed296b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 31 deletions

3
changelog/14301.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/kv: add full secret path output to table-formatted responses
```

View File

@ -158,6 +158,10 @@ func (c *KVGetCommand) Run(args []string) int {
tf.printWarnings(c.UI, secret)
}
if v2 {
outputPath(c.UI, path, "Secret Path")
}
if metadata, ok := secret.Data["metadata"]; ok && metadata != nil {
c.UI.Info(getHeaderForMap("Metadata", metadata.(map[string]interface{})))
OutputData(c.UI, metadata)

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
)
func kvReadRequest(client *api.Client, path string, params map[string]string) (*api.Secret, error) {
@ -141,6 +142,26 @@ func getHeaderForMap(header string, data map[string]interface{}) string {
// 4 for the column spaces and 5 for the len("value")
totalLen := maxKey + 4 + 5
return padEqualSigns(header, totalLen)
}
func kvParseVersionsFlags(versions []string) []string {
versionsOut := make([]string, 0, len(versions))
for _, v := range versions {
versionsOut = append(versionsOut, strutil.ParseStringSlice(v, ",")...)
}
return versionsOut
}
func outputPath(ui cli.Ui, path string, title string) {
ui.Info(padEqualSigns(title, len(path)))
ui.Info(path)
ui.Info("")
}
// Pad the table header with equal signs on each side
func padEqualSigns(header string, totalLen int) string {
equalSigns := totalLen - (len(header) + 2)
// If we have zero or fewer equal signs bump it back up to two on either
@ -156,12 +177,3 @@ func getHeaderForMap(header string, data map[string]interface{}) string {
return fmt.Sprintf("%s %s %s", strings.Repeat("=", equalSigns/2), header, strings.Repeat("=", equalSigns/2))
}
func kvParseVersionsFlags(versions []string) []string {
versionsOut := make([]string, 0, len(versions))
for _, v := range versions {
versionsOut = append(versionsOut, strutil.ParseStringSlice(v, ",")...)
}
return versionsOut
}

View File

@ -117,6 +117,8 @@ func (c *KVMetadataGetCommand) Run(args []string) int {
delete(secret.Data, "versions")
outputPath(c.UI, path, "Metadata Path")
c.UI.Info(getHeaderForMap("Metadata", secret.Data))
OutputSecret(c.UI, secret)

View File

@ -185,6 +185,13 @@ func (c *KVPatchCommand) Run(args []string) int {
return code
}
if Format(c.UI) == "table" {
outputPath(c.UI, path, "Secret Path")
metadata := secret.Data
c.UI.Info(getHeaderForMap("Metadata", metadata))
return OutputData(c.UI, metadata)
}
return OutputSecret(c.UI, secret)
}

View File

@ -161,5 +161,24 @@ func (c *KVPutCommand) Run(args []string) int {
return PrintRawField(c.UI, secret, c.flagField)
}
// if Format(c.UI) != "table" {
// return OutputSecret(c.UI, secret)
// }
// outputPath(c.UI, path, "Secret Path")
// metadata := secret.Data
// c.UI.Info(getHeaderForMap("Metadata", metadata))
// OutputData(c.UI, metadata)
// return 0
if Format(c.UI) == "table" {
outputPath(c.UI, path, "Secret Path")
metadata := secret.Data
c.UI.Info(getHeaderForMap("Metadata", metadata))
return OutputData(c.UI, metadata)
}
return OutputSecret(c.UI, secret)
}

View File

@ -134,6 +134,12 @@ func TestKVPutCommand(t *testing.T) {
v2ExpectedFields,
0,
},
{
"v2_secret_path",
[]string{"kv/write/foo", "foo=bar"},
[]string{"== Secret Path ==", "kv/data/write/foo"},
0,
},
}
for _, tc := range cases {
@ -872,6 +878,13 @@ func TestKVPatchCommand_RWMethodSucceeds(t *testing.T) {
}
}
// Test that full path was output
for _, str := range []string{"== Secret Path ==", "kv/data/patch/foo"} {
if !strings.Contains(combined, str) {
t.Errorf("expected %q to contain %q", combined, str)
}
}
// Test multi value
args = []string{"-method", "rw", "kv/patch/foo", "foo=aaa", "bar=bbb"}
code, combined = kvPatchWithRetry(t, client, args, nil)
@ -1106,28 +1119,6 @@ func TestKVPatchCommand_403Fallback(t *testing.T) {
}
}
func createTokenForPolicy(t *testing.T, client *api.Client, policy string) (*api.SecretAuth, error) {
t.Helper()
if err := client.Sys().PutPolicy("policy", policy); err != nil {
return nil, err
}
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"policy"},
TTL: "30m",
})
if err != nil {
return nil, err
}
if secret == nil || secret.Auth == nil || secret.Auth.ClientToken == "" {
return nil, fmt.Errorf("missing auth data: %#v", secret)
}
return secret.Auth, err
}
func TestKVPatchCommand_RWMethodPolicyVariations(t *testing.T) {
cases := []struct {
name string
@ -1205,3 +1196,73 @@ func TestKVPatchCommand_RWMethodPolicyVariations(t *testing.T) {
})
}
}
func TestPadEqualSigns(t *testing.T) {
t.Parallel()
header := "Test Header"
cases := []struct {
name string
totalPathLen int
expectedCount int
}{
{
name: "path with even length",
totalPathLen: 20,
expectedCount: 4,
},
{
name: "path with odd length",
totalPathLen: 19,
expectedCount: 3,
},
{
name: "smallest possible path",
totalPathLen: 8,
expectedCount: 2,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
padded := padEqualSigns(header, tc.totalPathLen)
signs := strings.Split(padded, fmt.Sprintf(" %s ", header))
if len(signs[0]) != len(signs[1]) {
t.Fatalf("expected an equal number of equal signs on both sides")
}
for _, sign := range signs {
count := strings.Count(sign, "=")
if count != tc.expectedCount {
t.Fatalf("expected %d equal signs but there were %d", tc.expectedCount, count)
}
}
})
}
}
func createTokenForPolicy(t *testing.T, client *api.Client, policy string) (*api.SecretAuth, error) {
t.Helper()
if err := client.Sys().PutPolicy("policy", policy); err != nil {
return nil, err
}
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"policy"},
TTL: "30m",
})
if err != nil {
return nil, err
}
if secret == nil || secret.Auth == nil || secret.Auth.ClientToken == "" {
return nil, fmt.Errorf("missing auth data: %#v", secret)
}
return secret.Auth, err
}