open-vault/command/base_predict_test.go

744 lines
13 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"reflect"
"testing"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/api"
"github.com/posener/complete"
)
func TestPredictVaultPaths(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
data := map[string]interface{}{"a": "b"}
if _, err := client.Logical().Write("secret/bar", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/foo", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/zip/zap", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/zip/zonk", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/zip/twoot", data); err != nil {
t.Fatal(err)
}
if err := client.Sys().Mount("level1a/level2a/level3a", &api.MountInput{Type: "kv"}); err != nil {
t.Fatal(err)
}
if err := client.Sys().Mount("level1a/level2a/level3b", &api.MountInput{Type: "kv"}); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
args complete.Args
includeFiles bool
exp []string
}{
{
"has_args",
complete.Args{
All: []string{"read", "secret/foo", "a=b"},
Last: "a=b",
},
true,
nil,
},
{
"has_args_no_files",
complete.Args{
All: []string{"read", "secret/foo", "a=b"},
Last: "a=b",
},
false,
nil,
},
{
"part_mount",
complete.Args{
All: []string{"read", "s"},
Last: "s",
},
true,
[]string{"secret/", "sys/"},
},
{
"part_mount_no_files",
complete.Args{
All: []string{"read", "s"},
Last: "s",
},
false,
[]string{"secret/", "sys/"},
},
{
"only_mount",
complete.Args{
All: []string{"read", "sec"},
Last: "sec",
},
true,
[]string{"secret/bar", "secret/foo", "secret/zip/"},
},
{
"only_mount_no_files",
complete.Args{
All: []string{"read", "sec"},
Last: "sec",
},
false,
[]string{"secret/zip/"},
},
{
"full_mount",
complete.Args{
All: []string{"read", "secret"},
Last: "secret",
},
true,
[]string{"secret/bar", "secret/foo", "secret/zip/"},
},
{
"full_mount_no_files",
complete.Args{
All: []string{"read", "secret"},
Last: "secret",
},
false,
[]string{"secret/zip/"},
},
{
"full_mount_slash",
complete.Args{
All: []string{"read", "secret/"},
Last: "secret/",
},
true,
[]string{"secret/bar", "secret/foo", "secret/zip/"},
},
{
"full_mount_slash_no_files",
complete.Args{
All: []string{"read", "secret/"},
Last: "secret/",
},
false,
[]string{"secret/zip/"},
},
{
"path_partial",
complete.Args{
All: []string{"read", "secret/z"},
Last: "secret/z",
},
true,
[]string{"secret/zip/twoot", "secret/zip/zap", "secret/zip/zonk"},
},
{
"path_partial_no_files",
complete.Args{
All: []string{"read", "secret/z"},
Last: "secret/z",
},
false,
[]string{"secret/zip/"},
},
{
"subpath_partial_z",
complete.Args{
All: []string{"read", "secret/zip/z"},
Last: "secret/zip/z",
},
true,
[]string{"secret/zip/zap", "secret/zip/zonk"},
},
{
"subpath_partial_z_no_files",
complete.Args{
All: []string{"read", "secret/zip/z"},
Last: "secret/zip/z",
},
false,
[]string{"secret/zip/z"},
},
{
"subpath_partial_t",
complete.Args{
All: []string{"read", "secret/zip/t"},
Last: "secret/zip/t",
},
true,
[]string{"secret/zip/twoot"},
},
{
"subpath_partial_t_no_files",
complete.Args{
All: []string{"read", "secret/zip/t"},
Last: "secret/zip/t",
},
false,
[]string{"secret/zip/t"},
},
{
"multi_nested",
complete.Args{
All: []string{"read", "level1a/level2a"},
Last: "level1a/level2a",
},
false,
[]string{
"level1a/level2a/level3a/",
"level1a/level2a/level3b/",
},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = client
f := p.vaultPaths(tc.includeFiles)
act := f(tc.args)
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_Audits(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
Type: "file",
Options: map[string]string{
"file_path": "discard",
},
}); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
client *api.Client
exp []string
}{
{
"not_connected_client",
badClient,
nil,
},
{
"good_path",
client,
[]string{"file/"},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.audits()
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_Mounts(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
cases := []struct {
name string
client *api.Client
exp []string
}{
{
"not_connected_client",
badClient,
defaultPredictVaultMounts,
},
{
"good_path",
client,
[]string{"cubbyhole/", "identity/", "secret/", "sys/"},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.mounts()
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_Plugins(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
cases := []struct {
name string
client *api.Client
exp []string
}{
{
"not_connected_client",
badClient,
nil,
},
{
"good_path",
client,
[]string{
"ad",
"alicloud",
"approle",
"aws",
"azure",
"cassandra-database-plugin",
"centrify",
"cert",
"cf",
"consul",
"couchbase-database-plugin",
"elasticsearch-database-plugin",
"gcp",
"gcpkms",
"github",
"hana-database-plugin",
"influxdb-database-plugin",
"jwt",
"kerberos",
"keymgmt",
"kmip",
"kubernetes",
"kv",
"ldap",
"mongodb-database-plugin",
"mongodbatlas",
"mongodbatlas-database-plugin",
"mssql-database-plugin",
"mysql-aurora-database-plugin",
"mysql-database-plugin",
"mysql-legacy-database-plugin",
"mysql-rds-database-plugin",
"nomad",
"oci",
"oidc",
"okta",
"openldap",
"pcf", // Deprecated.
"pki",
"postgresql-database-plugin",
"rabbitmq",
"radius",
"redis-database-plugin",
"redis-elasticache-database-plugin",
"redshift-database-plugin",
"snowflake-database-plugin",
"ssh",
"terraform",
"totp",
"transform",
"transit",
"userpass",
},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.plugins()
if !strutil.StrListContains(act, "keymgmt") {
for i, v := range tc.exp {
if v == "keymgmt" {
tc.exp = append(tc.exp[:i], tc.exp[i+1:]...)
break
}
}
}
if !strutil.StrListContains(act, "kmip") {
for i, v := range tc.exp {
if v == "kmip" {
tc.exp = append(tc.exp[:i], tc.exp[i+1:]...)
break
}
}
}
if !strutil.StrListContains(act, "transform") {
for i, v := range tc.exp {
if v == "transform" {
tc.exp = append(tc.exp[:i], tc.exp[i+1:]...)
break
}
}
}
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected: %q, got: %q, diff: %v", tc.exp, act, strutil.Difference(act, tc.exp, true))
}
})
}
})
}
func TestPredict_Policies(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
cases := []struct {
name string
client *api.Client
exp []string
}{
{
"not_connected_client",
badClient,
nil,
},
{
"good_path",
client,
[]string{"default", "root"},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.policies()
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_Paths(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
data := map[string]interface{}{"a": "b"}
if _, err := client.Logical().Write("secret/bar", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/foo", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/zip/zap", data); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
path string
includeFiles bool
exp []string
}{
{
"bad_path",
"nope/not/a/real/path/ever",
true,
[]string{"nope/not/a/real/path/ever"},
},
{
"good_path",
"secret/",
true,
[]string{"secret/bar", "secret/foo", "secret/zip/"},
},
{
"good_path_no_files",
"secret/",
false,
[]string{"secret/zip/"},
},
{
"partial_match",
"secret/z",
true,
[]string{"secret/zip/"},
},
{
"partial_match_no_files",
"secret/z",
false,
[]string{"secret/zip/"},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = client
act := p.paths("kv", "1", tc.path, tc.includeFiles)
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_PathsKVv2(t *testing.T) {
t.Parallel()
client, closer := testVaultServerWithKVVersion(t, "2")
defer closer()
data := map[string]interface{}{"data": map[string]interface{}{"a": "b"}}
if _, err := client.Logical().Write("secret/data/bar", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/data/foo", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/data/zip/zap", data); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
path string
includeFiles bool
exp []string
}{
{
"bad_path",
"nope/not/a/real/path/ever",
true,
[]string{"nope/not/a/real/path/ever"},
},
{
"good_path",
"secret/",
true,
[]string{"secret/bar", "secret/foo", "secret/zip/"},
},
{
"good_path_no_files",
"secret/",
false,
[]string{"secret/zip/"},
},
{
"partial_match",
"secret/z",
true,
[]string{"secret/zip/"},
},
{
"partial_match_no_files",
"secret/z",
false,
[]string{"secret/zip/"},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = client
act := p.paths("kv", "2", tc.path, tc.includeFiles)
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_ListPaths(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
data := map[string]interface{}{"a": "b"}
if _, err := client.Logical().Write("secret/bar", data); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("secret/foo", data); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
client *api.Client
path string
exp []string
}{
{
"bad_path",
client,
"nope/not/a/real/path/ever",
nil,
},
{
"good_path",
client,
"secret/",
[]string{"bar", "foo"},
},
{
"not_connected_client",
badClient,
"secret/",
nil,
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.listPaths(tc.path)
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_HasPathArg(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
exp bool
}{
{
"nil",
nil,
false,
},
{
"empty",
[]string{},
false,
},
{
"empty_string",
[]string{""},
false,
},
{
"single",
[]string{"foo"},
false,
},
{
"multiple",
[]string{"foo", "bar", "baz"},
true,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
if act := p.hasPathArg(tc.args); act != tc.exp {
t.Errorf("expected %t to be %t", act, tc.exp)
}
})
}
}