diff --git a/command/kv_helpers_test.go b/command/kv_helpers_test.go index 5ab1f6e39..ca5f7e8c5 100644 --- a/command/kv_helpers_test.go +++ b/command/kv_helpers_test.go @@ -1,6 +1,13 @@ package command -import "testing" +import ( + "context" + "reflect" + "testing" + "time" + + "github.com/hashicorp/vault/api" +) // TestAddPrefixToKVPath tests the addPrefixToKVPath helper function func TestAddPrefixToKVPath(t *testing.T) { @@ -63,6 +70,8 @@ func TestAddPrefixToKVPath(t *testing.T) { for name, tc := range cases { name, tc := name, tc t.Run(name, func(t *testing.T) { + t.Parallel() + actual := addPrefixToKVPath( tc.path, tc.mountPath, @@ -76,3 +85,188 @@ func TestAddPrefixToKVPath(t *testing.T) { }) } } + +// TestWalkSecretsTree tests the walkSecretsTree helper function +func TestWalkSecretsTree(t *testing.T) { + // test setup + client, closer := testVaultServer(t) + defer closer() + + // enable kv-v1 backend + if err := client.Sys().Mount("kv-v1/", &api.MountInput{ + Type: "kv-v1", + }); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second) + + // enable kv-v2 backend + if err := client.Sys().Mount("kv-v2/", &api.MountInput{ + Type: "kv-v2", + }); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second) + + ctx, cancelContextFunc := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelContextFunc() + + // populate secrets + for _, path := range []string{ + "foo", + "app-1/foo", + "app-1/bar", + "app-1/nested/x/y/z", + "app-1/nested/x/y", + "app-1/nested/bar", + } { + if err := client.KVv1("kv-v1").Put(ctx, path, map[string]interface{}{ + "password": "Hashi123", + }); err != nil { + t.Fatal(err) + } + + if _, err := client.KVv2("kv-v2").Put(ctx, path, map[string]interface{}{ + "password": "Hashi123", + }); err != nil { + t.Fatal(err) + } + } + + type treePath struct { + path string + directory bool + } + + cases := map[string]struct { + path string + expected []treePath + expectedError bool + }{ + "kv-v1-simple": { + path: "kv-v1/app-1/nested/x/y", + expected: []treePath{ + {path: "kv-v1/app-1/nested/x/y/z", directory: false}, + }, + expectedError: false, + }, + + "kv-v2-simple": { + path: "kv-v2/metadata/app-1/nested/x/y", + expected: []treePath{ + {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, + }, + expectedError: false, + }, + + "kv-v1-nested": { + path: "kv-v1/app-1/nested/", + expected: []treePath{ + {path: "kv-v1/app-1/nested/bar", directory: false}, + {path: "kv-v1/app-1/nested/x", directory: true}, + {path: "kv-v1/app-1/nested/x/y", directory: false}, + {path: "kv-v1/app-1/nested/x/y", directory: true}, + {path: "kv-v1/app-1/nested/x/y/z", directory: false}, + }, + expectedError: false, + }, + + "kv-v2-nested": { + path: "kv-v2/metadata/app-1/nested/", + expected: []treePath{ + {path: "kv-v2/metadata/app-1/nested/bar", directory: false}, + {path: "kv-v2/metadata/app-1/nested/x", directory: true}, + {path: "kv-v2/metadata/app-1/nested/x/y", directory: false}, + {path: "kv-v2/metadata/app-1/nested/x/y", directory: true}, + {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, + }, + expectedError: false, + }, + + "kv-v1-all": { + path: "kv-v1", + expected: []treePath{ + {path: "kv-v1/app-1", directory: true}, + {path: "kv-v1/app-1/bar", directory: false}, + {path: "kv-v1/app-1/foo", directory: false}, + {path: "kv-v1/app-1/nested", directory: true}, + {path: "kv-v1/app-1/nested/bar", directory: false}, + {path: "kv-v1/app-1/nested/x", directory: true}, + {path: "kv-v1/app-1/nested/x/y", directory: false}, + {path: "kv-v1/app-1/nested/x/y", directory: true}, + {path: "kv-v1/app-1/nested/x/y/z", directory: false}, + {path: "kv-v1/foo", directory: false}, + }, + expectedError: false, + }, + + "kv-v2-all": { + path: "kv-v2/metadata", + expected: []treePath{ + {path: "kv-v2/metadata/app-1", directory: true}, + {path: "kv-v2/metadata/app-1/bar", directory: false}, + {path: "kv-v2/metadata/app-1/foo", directory: false}, + {path: "kv-v2/metadata/app-1/nested", directory: true}, + {path: "kv-v2/metadata/app-1/nested/bar", directory: false}, + {path: "kv-v2/metadata/app-1/nested/x", directory: true}, + {path: "kv-v2/metadata/app-1/nested/x/y", directory: false}, + {path: "kv-v2/metadata/app-1/nested/x/y", directory: true}, + {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, + {path: "kv-v2/metadata/foo", directory: false}, + }, + expectedError: false, + }, + + "kv-v1-not-found": { + path: "kv-v1/does/not/exist", + expected: nil, + expectedError: true, + }, + + "kv-v2-not-found": { + path: "kv-v2/metadata/does/not/exist", + expected: nil, + expectedError: true, + }, + + "kv-v1-not-listable-leaf-node": { + path: "kv-v1/foo", + expected: nil, + expectedError: true, + }, + + "kv-v2-not-listable-leaf-node": { + path: "kv-v2/metadata/foo", + expected: nil, + expectedError: true, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + var descendants []treePath + + err := walkSecretsTree(ctx, client, tc.path, func(path string, directory bool) error { + descendants = append(descendants, treePath{ + path: path, + directory: directory, + }) + return nil + }) + + if tc.expectedError { + if err == nil { + t.Fatal("an error was expected but the test succeeded") + } + } else { + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(tc.expected, descendants) { + t.Fatalf("unexpected list output; want: %v, got: %v", tc.expected, descendants) + } + } + }) + } +} diff --git a/command/kv_test.go b/command/kv_test.go index 4443f5e8b..6564208ed 100644 --- a/command/kv_test.go +++ b/command/kv_test.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io" - "reflect" "strings" "testing" "time" @@ -1524,193 +1523,6 @@ func TestPadEqualSigns(t *testing.T) { } } -// TestWalkSecretsTree the walkSecretsTree helper function -func TestWalkSecretsTree(t *testing.T) { - // test setup - client, closer := testVaultServer(t) - defer closer() - - // enable kv-v1 backend - if err := client.Sys().Mount("kv-v1/", &api.MountInput{ - Type: "kv-v1", - }); err != nil { - t.Fatal(err) - } - time.Sleep(time.Second) - - // enable kv-v2 backend - if err := client.Sys().Mount("kv-v2/", &api.MountInput{ - Type: "kv-v2", - }); err != nil { - t.Fatal(err) - } - time.Sleep(time.Second) - - ctx, cancelContextFunc := context.WithTimeout(context.Background(), 5*time.Second) - defer cancelContextFunc() - - // populate secrets - for _, path := range []string{ - "foo", - "app-1/foo", - "app-1/bar", - "app-1/nested/x/y/z", - "app-1/nested/x/y", - "app-1/nested/bar", - } { - if err := client.KVv1("kv-v1").Put(ctx, path, map[string]interface{}{ - "password": "Hashi123", - }); err != nil { - t.Fatal(err) - } - - if _, err := client.KVv2("kv-v2").Put(ctx, path, map[string]interface{}{ - "password": "Hashi123", - }); err != nil { - t.Fatal(err) - } - } - - type treePath struct { - path string - directory bool - } - - cases := []struct { - name string - path string - expected []treePath - expectedError bool - }{ - { - name: "kv-v1-simple", - path: "kv-v1/app-1/nested/x/y", - expected: []treePath{ - {path: "kv-v1/app-1/nested/x/y/z", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v2-simple", - path: "kv-v2/metadata/app-1/nested/x/y", - expected: []treePath{ - {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v1-nested", - path: "kv-v1/app-1/nested/", - expected: []treePath{ - {path: "kv-v1/app-1/nested/bar", directory: false}, - {path: "kv-v1/app-1/nested/x", directory: true}, - {path: "kv-v1/app-1/nested/x/y", directory: false}, - {path: "kv-v1/app-1/nested/x/y", directory: true}, - {path: "kv-v1/app-1/nested/x/y/z", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v2-nested", - path: "kv-v2/metadata/app-1/nested/", - expected: []treePath{ - {path: "kv-v2/metadata/app-1/nested/bar", directory: false}, - {path: "kv-v2/metadata/app-1/nested/x", directory: true}, - {path: "kv-v2/metadata/app-1/nested/x/y", directory: false}, - {path: "kv-v2/metadata/app-1/nested/x/y", directory: true}, - {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v1-all", - path: "kv-v1", - expected: []treePath{ - {path: "kv-v1/app-1", directory: true}, - {path: "kv-v1/app-1/bar", directory: false}, - {path: "kv-v1/app-1/foo", directory: false}, - {path: "kv-v1/app-1/nested", directory: true}, - {path: "kv-v1/app-1/nested/bar", directory: false}, - {path: "kv-v1/app-1/nested/x", directory: true}, - {path: "kv-v1/app-1/nested/x/y", directory: false}, - {path: "kv-v1/app-1/nested/x/y", directory: true}, - {path: "kv-v1/app-1/nested/x/y/z", directory: false}, - {path: "kv-v1/foo", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v2-all", - path: "kv-v2/metadata", - expected: []treePath{ - {path: "kv-v2/metadata/app-1", directory: true}, - {path: "kv-v2/metadata/app-1/bar", directory: false}, - {path: "kv-v2/metadata/app-1/foo", directory: false}, - {path: "kv-v2/metadata/app-1/nested", directory: true}, - {path: "kv-v2/metadata/app-1/nested/bar", directory: false}, - {path: "kv-v2/metadata/app-1/nested/x", directory: true}, - {path: "kv-v2/metadata/app-1/nested/x/y", directory: false}, - {path: "kv-v2/metadata/app-1/nested/x/y", directory: true}, - {path: "kv-v2/metadata/app-1/nested/x/y/z", directory: false}, - {path: "kv-v2/metadata/foo", directory: false}, - }, - expectedError: false, - }, - { - name: "kv-v1-not-found", - path: "kv-v1/does/not/exist", - expected: nil, - expectedError: true, - }, - { - name: "kv-v2-not-found", - path: "kv-v2/metadata/does/not/exist", - expected: nil, - expectedError: true, - }, - { - name: "kv-v1-not-listable-leaf-node", - path: "kv-v1/foo", - expected: nil, - expectedError: true, - }, - { - name: "kv-v2-not-listable-leaf-node", - path: "kv-v2/metadata/foo", - expected: nil, - expectedError: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - var descendants []treePath - - err := walkSecretsTree(ctx, client, tc.path, func(path string, directory bool) error { - descendants = append(descendants, treePath{ - path: path, - directory: directory, - }) - return nil - }) - - if tc.expectedError { - if err == nil { - t.Fatal("an error was expected but the test succeeded") - } - } else { - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(tc.expected, descendants) { - t.Fatalf("unexpected list output; want: %v, got: %v", tc.expected, descendants) - } - } - }) - } -} - func createTokenForPolicy(t *testing.T, client *api.Client, policy string) (*api.SecretAuth, error) { t.Helper()