mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			273 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package command
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/api"
 | |
| )
 | |
| 
 | |
| // TestAddPrefixToKVPath tests the addPrefixToKVPath helper function
 | |
| func TestAddPrefixToKVPath(t *testing.T) {
 | |
| 	cases := map[string]struct {
 | |
| 		path         string
 | |
| 		mountPath    string
 | |
| 		apiPrefix    string
 | |
| 		skipIfExists bool
 | |
| 		expected     string
 | |
| 	}{
 | |
| 		"simple": {
 | |
| 			path:         "kv-v2/foo",
 | |
| 			mountPath:    "kv-v2/",
 | |
| 			apiPrefix:    "data",
 | |
| 			skipIfExists: false,
 | |
| 			expected:     "kv-v2/data/foo",
 | |
| 		},
 | |
| 
 | |
| 		"multi-part": {
 | |
| 			path:         "my/kv-v2/mount/path/foo/bar/baz",
 | |
| 			mountPath:    "my/kv-v2/mount/path",
 | |
| 			apiPrefix:    "metadata",
 | |
| 			skipIfExists: false,
 | |
| 			expected:     "my/kv-v2/mount/path/metadata/foo/bar/baz",
 | |
| 		},
 | |
| 
 | |
| 		"with-namespace": {
 | |
| 			path:         "my/kv-v2/mount/path/foo/bar/baz",
 | |
| 			mountPath:    "my/ns1/my/kv-v2/mount/path",
 | |
| 			apiPrefix:    "metadata",
 | |
| 			skipIfExists: false,
 | |
| 			expected:     "my/kv-v2/mount/path/metadata/foo/bar/baz",
 | |
| 		},
 | |
| 
 | |
| 		"skip-if-exists-true": {
 | |
| 			path:         "kv-v2/data/foo",
 | |
| 			mountPath:    "kv-v2/",
 | |
| 			apiPrefix:    "data",
 | |
| 			skipIfExists: true,
 | |
| 			expected:     "kv-v2/data/foo",
 | |
| 		},
 | |
| 
 | |
| 		"skip-if-exists-false": {
 | |
| 			path:         "kv-v2/data/foo",
 | |
| 			mountPath:    "kv-v2",
 | |
| 			apiPrefix:    "data",
 | |
| 			skipIfExists: false,
 | |
| 			expected:     "kv-v2/data/data/foo",
 | |
| 		},
 | |
| 
 | |
| 		"skip-if-exists-with-namespace": {
 | |
| 			path:         "my/kv-v2/mount/path/metadata/foo/bar/baz",
 | |
| 			mountPath:    "my/ns1/my/kv-v2/mount/path",
 | |
| 			apiPrefix:    "metadata",
 | |
| 			skipIfExists: true,
 | |
| 			expected:     "my/kv-v2/mount/path/metadata/foo/bar/baz",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for name, tc := range cases {
 | |
| 		name, tc := name, tc
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 
 | |
| 			actual := addPrefixToKVPath(
 | |
| 				tc.path,
 | |
| 				tc.mountPath,
 | |
| 				tc.apiPrefix,
 | |
| 				tc.skipIfExists,
 | |
| 			)
 | |
| 
 | |
| 			if tc.expected != actual {
 | |
| 				t.Fatalf("unexpected output; want: %v, got: %v", tc.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
