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:
parent
b9a6ec67c9
commit
63a2ed296b
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
secrets/kv: add full secret path output to table-formatted responses
|
||||
```
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue