Skip to content

Commit

Permalink
Output full secret path in certain kv commands (#14301)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
digivava committed Mar 8, 2022
1 parent f142cb4 commit 191868d
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 31 deletions.
3 changes: 3 additions & 0 deletions changelog/14301.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
secrets/kv: add full secret path output to table-formatted responses
```
4 changes: 4 additions & 0 deletions command/kv_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 21 additions & 9 deletions command/kv_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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
}
2 changes: 2 additions & 0 deletions command/kv_metadata_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
7 changes: 7 additions & 0 deletions command/kv_patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
19 changes: 19 additions & 0 deletions command/kv_put.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
105 changes: 83 additions & 22 deletions command/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

0 comments on commit 191868d

Please sign in to comment.