Update mount table and CLI with plugin version for auth (#16856)

This commit is contained in:
Christopher Swenson 2022-08-31 11:23:05 -07:00 committed by GitHub
parent 96b97017b1
commit 09ad6ab72c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1479 additions and 328 deletions

View File

@ -247,6 +247,7 @@ type MountInput struct {
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
ExternalEntropyAccess bool `json:"external_entropy_access" mapstructure:"external_entropy_access"`
Options map[string]string `json:"options"`
Version string `json:"version,omitempty"`
// Deprecated: Newer server responses should be returning this information in the
// Type field (json: "type") instead.
@ -281,6 +282,10 @@ type MountOutput struct {
Local bool `json:"local"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
ExternalEntropyAccess bool `json:"external_entropy_access" mapstructure:"external_entropy_access"`
Version string `json:"version"`
RunningVersion string `json:"running_version"`
Sha string `json:"sha"`
RunningSha string `json:"running_sha"`
}
type MountConfigOutput struct {

153
api/sys_mounts_test.go Normal file
View File

@ -0,0 +1,153 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestListMounts(t *testing.T) {
mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultMountsHandler))
defer mockVaultServer.Close()
cfg := DefaultConfig()
cfg.Address = mockVaultServer.URL
client, err := NewClient(cfg)
if err != nil {
t.Fatal(err)
}
resp, err := client.Sys().ListMounts()
if err != nil {
t.Fatal(err)
}
expectedMounts := map[string]struct {
Type string
Version string
}{
"cubbyhole/": {Type: "cubbyhole", Version: "v1.0.0"},
"identity/": {Type: "identity", Version: ""},
"secret/": {Type: "kv", Version: ""},
"sys/": {Type: "system", Version: ""},
}
for path, mount := range resp {
expected, ok := expectedMounts[path]
if !ok {
t.Errorf("Unexpected mount: %s: %+v", path, mount)
continue
}
if expected.Type != mount.Type || expected.Version != mount.Version {
t.Errorf("Mount did not match: %s -> expected %+v but got %+v", path, expected, mount)
}
}
for path, expected := range expectedMounts {
mount, ok := resp[path]
if !ok {
t.Errorf("Expected mount not found mount: %s: %+v", path, expected)
continue
}
if expected.Type != mount.Type || expected.Version != mount.Version {
t.Errorf("Mount did not match: %s -> expected %+v but got %+v", path, expected, mount)
}
}
}
func mockVaultMountsHandler(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(listMountsResponse))
}
const listMountsResponse = `{
"request_id": "3cd881e9-ea50-2e06-90b2-5641667485fa",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"cubbyhole/": {
"accessor": "cubbyhole_2e3fc28d",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0
},
"description": "per-token private secret storage",
"external_entropy_access": false,
"local": true,
"options": null,
"running_sha": "",
"running_version": "",
"seal_wrap": false,
"sha": "",
"type": "cubbyhole",
"uuid": "575063dc-5ef8-4487-c842-22c494c19a6f",
"version": "v1.0.0"
},
"identity/": {
"accessor": "identity_6e01c327",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"passthrough_request_headers": [
"Authorization"
]
},
"description": "identity store",
"external_entropy_access": false,
"local": false,
"options": null,
"running_sha": "",
"running_version": "",
"seal_wrap": false,
"sha": "",
"type": "identity",
"uuid": "187d7eba-3471-554b-c2d9-1479612c8046",
"version": ""
},
"secret/": {
"accessor": "kv_3e2f282f",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0
},
"description": "key/value secret storage",
"external_entropy_access": false,
"local": false,
"options": {
"version": "2"
},
"running_sha": "",
"running_version": "",
"seal_wrap": false,
"sha": "",
"type": "kv",
"uuid": "13375e0f-876e-7e96-0a3e-076f37b6b69d",
"version": ""
},
"sys/": {
"accessor": "system_93503264",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"passthrough_request_headers": [
"Accept"
]
},
"description": "system endpoints used for control, policy and debugging",
"external_entropy_access": false,
"local": false,
"options": null,
"running_sha": "",
"running_version": "",
"seal_wrap": true,
"sha": "",
"type": "system",
"uuid": "1373242d-cc4d-c023-410b-7f336e7ba0a8",
"version": ""
}
}
}`

View File

@ -22,6 +22,8 @@ type ListPluginsResponse struct {
// PluginsByType is the list of plugins by type.
PluginsByType map[consts.PluginType][]string `json:"types"`
Details []PluginDetails `json:"details,omitempty"`
// Names is the list of names of the plugins.
//
// Deprecated: Newer server responses should be returning PluginsByType (json:
@ -29,6 +31,13 @@ type ListPluginsResponse struct {
Names []string `json:"names"`
}
type PluginDetails struct {
Type string `json:"string"`
Name string `json:"name"`
Version string `json:"version,omitempty"`
Builtin bool `json:"builtin"`
}
// ListPlugins wraps ListPluginsWithContext using context.Background.
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
return c.ListPluginsWithContext(context.Background(), i)
@ -98,6 +107,7 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
result := &ListPluginsResponse{
PluginsByType: make(map[consts.PluginType][]string),
Details: []PluginDetails{},
}
if i.Type == consts.PluginTypeUnknown {
for _, pluginType := range consts.PluginTypes {
@ -129,6 +139,12 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
result.PluginsByType[i.Type] = respKeys
}
if detailed, ok := secret.Data["detailed"]; ok {
if err := mapstructure.Decode(detailed, &result.Details); err != nil {
return nil, err
}
}
return result, nil
}
@ -194,6 +210,9 @@ type RegisterPluginInput struct {
// SHA256 is the shasum of the plugin.
SHA256 string `json:"sha256,omitempty"`
// Version is the optional version of the plugin being registered
Version string `json:"version,omitempty"`
}
// RegisterPlugin wraps RegisterPluginWithContext using context.Background.
@ -227,6 +246,9 @@ type DeregisterPluginInput struct {
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
// Version of the plugin. Optional.
Version string `json:"version,omitempty"`
}
// DeregisterPlugin wraps DeregisterPluginWithContext using context.Background.
@ -242,7 +264,7 @@ func (c *Sys) DeregisterPluginWithContext(ctx context.Context, i *DeregisterPlug
path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodDelete, path)
req.Params.Set("version", i.Version)
resp, err := c.c.rawRequestWithContext(ctx, req)
if err == nil {
defer resp.Body.Close()

View File

@ -9,8 +9,27 @@ import (
"github.com/hashicorp/vault/sdk/helper/consts"
)
func TestRegisterPlugin(t *testing.T) {
mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerRegister))
defer mockVaultServer.Close()
cfg := DefaultConfig()
cfg.Address = mockVaultServer.URL
client, err := NewClient(cfg)
if err != nil {
t.Fatal(err)
}
err = client.Sys().RegisterPluginWithContext(context.Background(), &RegisterPluginInput{
Version: "v1.0.0",
})
if err != nil {
t.Fatal(err)
}
}
func TestListPlugins(t *testing.T) {
mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandler))
mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerList))
defer mockVaultServer.Close()
cfg := DefaultConfig()
@ -44,7 +63,7 @@ func TestListPlugins(t *testing.T) {
}
}
func mockVaultHandler(w http.ResponseWriter, _ *http.Request) {
func mockVaultHandlerList(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(listUntypedResponse))
}
@ -77,3 +96,9 @@ const listUntypedResponse = `{
"warnings": null,
"auth": null
}`
func mockVaultHandlerRegister(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(registerResponse))
}
const registerResponse = `{}`

View File

@ -62,11 +62,12 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (*PluginBackend,
if err != nil {
return nil, err
}
version := conf.Config["plugin_version"]
sys := conf.System
// NewBackend with isMetadataMode set to true
raw, err := bplugin.NewBackend(ctx, name, pluginType, sys, conf, true)
// NewBackendWithVersion with isMetadataMode set to true
raw, err := bplugin.NewBackendWithVersion(ctx, name, pluginType, sys, conf, true, version)
if err != nil {
return nil, err
}
@ -119,7 +120,7 @@ func (b *PluginBackend) startBackend(ctx context.Context, storage logical.Storag
// Ensure proper cleanup of the backend (i.e. call client.Kill())
b.Backend.Cleanup(ctx)
nb, err := bplugin.NewBackend(ctx, pluginName, pluginType, b.config.System, b.config, false)
nb, err := bplugin.NewBackendWithVersion(ctx, pluginName, pluginType, b.config.System, b.config, false, b.config.Config["plugin_version"])
if err != nil {
return err
}

View File

@ -183,3 +183,13 @@ func (v testSystemView) LookupPlugin(context.Context, string, consts.PluginType)
},
}, nil
}
func (v testSystemView) LookupPluginVersion(context.Context, string, consts.PluginType, string) (*pluginutil.PluginRunner, error) {
return &pluginutil.PluginRunner{
Name: "test-plugin-runner",
Builtin: true,
BuiltinFactory: func() (interface{}, error) {
return v.factory, nil
},
}, nil
}

3
changelog/16856.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:change
plugins: Add plugin version to auth register, list, and mount table
```

View File

@ -37,6 +37,7 @@ type AuthEnableCommand struct {
flagExternalEntropyAccess bool
flagTokenType string
flagVersion int
flagPluginVersion string
}
func (c *AuthEnableCommand) Synopsis() string {
@ -199,6 +200,13 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Usage: "Select the version of the auth method to run. Not supported by all auth methods.",
})
f.StringVar(&StringVar{
Name: "plugin-version",
Target: &c.flagPluginVersion,
Default: "",
Usage: "Select the version of the plugin to enable.",
})
return set
}
@ -262,6 +270,7 @@ func (c *AuthEnableCommand) Run(args []string) int {
authOpts := &api.EnableAuthOptions{
Type: authType,
Version: c.flagPluginVersion,
Description: c.flagDescription,
Local: c.flagLocal,
SealWrap: c.flagSealWrap,

View File

@ -118,10 +118,10 @@ func (c *AuthListCommand) simpleMounts(auths map[string]*api.AuthMount) []string
}
sort.Strings(paths)
out := []string{"Path | Type | Accessor | Description"}
out := []string{"Path | Type | Accessor | Description | Version"}
for _, path := range paths {
mount := auths[path]
out = append(out, fmt.Sprintf("%s | %s | %s | %s", path, mount.Type, mount.Accessor, mount.Description))
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s", path, mount.Type, mount.Accessor, mount.Description, mount.Version))
}
return out
@ -145,7 +145,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
}
}
out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID"}
out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID | Version"}
for _, path := range paths {
mount := auths[path]
@ -162,7 +162,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
pluginName = mount.Config.PluginName
}
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s",
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s | %s",
path,
pluginName,
mount.Accessor,
@ -175,6 +175,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
mount.Options,
mount.Description,
mount.UUID,
mount.Version,
))
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"strings"
semver "github.com/hashicorp/go-version"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/mitchellh/cli"
@ -17,6 +18,8 @@ var (
type PluginDeregisterCommand struct {
*BaseCommand
flagVersion string
}
func (c *PluginDeregisterCommand) Synopsis() string {
@ -28,20 +31,36 @@ func (c *PluginDeregisterCommand) Help() string {
Usage: vault plugin deregister [options] TYPE NAME
Deregister an existing plugin in the catalog. If the plugin does not exist,
no action is taken (the command is idempotent). The argument of type
no action is taken (the command is idempotent). The TYPE argument
takes "auth", "database", or "secret".
Deregister the plugin named my-custom-plugin:
Deregister the unversioned auth plugin named my-custom-plugin:
$ vault plugin deregister auth my-custom-plugin
Deregister the auth plugin named my-custom-plugin, version 1.0.0:
$ vault plugin deregister -version=v1.0.0 auth my-custom-plugin
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *PluginDeregisterCommand) Flags() *FlagSets {
return c.flagSet(FlagSetHTTP)
set := c.flagSet(FlagSetHTTP)
f := set.NewFlagSet("Command Options")
f.StringVar(&StringVar{
Name: "version",
Target: &c.flagVersion,
Completion: complete.PredictAnything,
Usage: "Version of the plugin to deregister. If unset, " +
"only an unversioned plugin may be deregistered.",
})
return set
}
func (c *PluginDeregisterCommand) AutocompleteArgs() complete.Predictor {
@ -62,21 +81,19 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
var pluginNameRaw, pluginTypeRaw string
args = f.Args()
switch {
case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args)))
switch len(args) {
case 0:
c.UI.Error("Not enough arguments (expected 1, or 2, got 0)")
return 1
case len(args) > 2:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args)))
return 1
// These cases should come after invalid cases have been checked
case len(args) == 1:
case 1:
pluginTypeRaw = "unknown"
pluginNameRaw = args[0]
case len(args) == 2:
case 2:
pluginTypeRaw = args[0]
pluginNameRaw = args[1]
default:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, or 2, got %d)", len(args)))
return 1
}
client, err := c.Client()
@ -91,10 +108,18 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
return 2
}
pluginName := strings.TrimSpace(pluginNameRaw)
if c.flagVersion != "" {
_, err := semver.NewSemver(c.flagVersion)
if err != nil {
c.UI.Error(fmt.Sprintf("version %q is not a valid semantic version: %v", c.flagVersion, err))
return 2
}
}
if err := client.Sys().DeregisterPlugin(&api.DeregisterPluginInput{
Name: pluginName,
Type: pluginType,
Name: pluginName,
Type: pluginType,
Version: c.flagVersion,
}); err != nil {
c.UI.Error(fmt.Sprintf("Error deregistering plugin named %s: %s", pluginName, err))
return 2

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@ -76,7 +77,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
@ -131,6 +132,101 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
}
})
t.Run("integration with version", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
defer closer()
pluginName := "my-plugin"
_, _, version := testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)
ui, cmd := testPluginDeregisterCommand(t)
cmd.client = client
code := cmd.Run([]string{
"-version=" + version,
consts.PluginTypeCredential.String(),
pluginName,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Deregistered plugin (if it was registered): "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{
Type: consts.PluginTypeUnknown,
})
if err != nil {
t.Fatal(err)
}
found := false
for _, p := range resp.Details {
if p.Name == pluginName {
found = true
}
}
if found {
t.Errorf("expected %q to not be in %#v", pluginName, resp.Details)
}
})
t.Run("integration with missing version", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
defer closer()
pluginName := "my-plugin"
testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)
ui, cmd := testPluginDeregisterCommand(t)
cmd.client = client
code := cmd.Run([]string{
consts.PluginTypeCredential.String(),
pluginName,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Deregistered plugin (if it was registered): "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{
Type: consts.PluginTypeUnknown,
})
if err != nil {
t.Fatal(err)
}
found := false
for _, p := range resp.Details {
if p.Name == pluginName {
found = true
}
}
if !found {
t.Errorf("expected %q to be in %#v", pluginName, resp.Details)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@ -73,7 +74,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
t.Run("default", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
@ -104,7 +105,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
t.Run("field", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@ -21,6 +21,7 @@ type PluginRegisterCommand struct {
flagArgs []string
flagCommand string
flagSHA256 string
flagVersion string
}
func (c *PluginRegisterCommand) Synopsis() string {
@ -37,12 +38,13 @@ Usage: vault plugin register [options] TYPE NAME
Register the plugin named my-custom-plugin:
$ vault plugin register -sha256=d3f0a8b... auth my-custom-plugin
$ vault plugin register -sha256=d3f0a8b... -version=v1.0.0 auth my-custom-plugin
Register a plugin with custom arguments:
$ vault plugin register \
-sha256=d3f0a8b... \
-version=v1.0.0 \
-args=--with-glibc,--with-cgo \
auth my-custom-plugin
@ -79,6 +81,13 @@ func (c *PluginRegisterCommand) Flags() *FlagSets {
Usage: "SHA256 of the plugin binary. This is required for all plugins.",
})
f.StringVar(&StringVar{
Name: "version",
Target: &c.flagVersion,
Completion: complete.PredictAnything,
Usage: "Version of the plugin. Optional.",
})
return set
}
@ -144,6 +153,7 @@ func (c *PluginRegisterCommand) Run(args []string) int {
Args: c.flagArgs,
Command: command,
SHA256: c.flagSHA256,
Version: c.flagVersion,
}); err != nil {
c.UI.Error(fmt.Sprintf("Error registering plugin %s: %s", pluginName, err))
return 2

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@ -77,7 +78,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@ -82,7 +83,7 @@ func TestPluginReloadCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@ -6,36 +6,12 @@ import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
)
// testPluginDir creates a temporary directory suitable for holding plugins.
// This helper also resolves symlinks to make tests happy on OS X.
func testPluginDir(tb testing.TB) (string, func(tb testing.TB)) {
tb.Helper()
dir, err := ioutil.TempDir("", "")
if err != nil {
tb.Fatal(err)
}
// OSX tempdir are /var, but actually symlinked to /private/var
dir, err = filepath.EvalSymlinks(dir)
if err != nil {
tb.Fatal(err)
}
return dir, func(tb testing.TB) {
if err := os.RemoveAll(dir); err != nil {
tb.Fatal(err)
}
}
}
// testPluginCreate creates a sample plugin in a tempdir and returns the shasum
// and filepath to the plugin.
func testPluginCreate(tb testing.TB, dir, name string) (string, string) {
@ -78,3 +54,22 @@ func testPluginCreateAndRegister(tb testing.TB, client *api.Client, dir, name st
return pth, sha256Sum
}
// testPluginCreateAndRegisterVersioned creates a versioned plugin and registers it in the catalog.
func testPluginCreateAndRegisterVersioned(tb testing.TB, client *api.Client, dir, name string, pluginType consts.PluginType) (string, string, string) {
tb.Helper()
pth, sha256Sum := testPluginCreate(tb, dir, name)
if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{
Name: name,
Type: pluginType,
Command: name,
SHA256: sha256Sum,
Version: "v1.0.0",
}); err != nil {
tb.Fatal(err)
}
return pth, sha256Sum, "v1.0.0"
}

View File

@ -410,9 +410,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -424,9 +428,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -437,9 +445,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -451,9 +463,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"secret/": map[string]interface{}{
@ -465,9 +481,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -479,9 +499,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -492,9 +516,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -506,9 +534,13 @@ func TestSysMounts_headerAuth(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)

View File

@ -38,9 +38,13 @@ func TestSysAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"token/": map[string]interface{}{
@ -53,9 +57,13 @@ func TestSysAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -114,9 +122,13 @@ func TestSysEnableAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"token/": map[string]interface{}{
"description": "token based credentials",
@ -128,9 +140,13 @@ func TestSysEnableAuth(t *testing.T) {
"force_no_cache": false,
"token_type": "default-service",
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"foo/": map[string]interface{}{
@ -143,9 +159,13 @@ func TestSysEnableAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"token/": map[string]interface{}{
"description": "token based credentials",
@ -157,9 +177,13 @@ func TestSysEnableAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -224,6 +248,10 @@ func TestSysDisableAuth(t *testing.T) {
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"token/": map[string]interface{}{
@ -239,6 +267,10 @@ func TestSysDisableAuth(t *testing.T) {
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -492,9 +524,13 @@ func TestSysRemountAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"token/": map[string]interface{}{
"description": "token based credentials",
@ -506,9 +542,13 @@ func TestSysRemountAuth(t *testing.T) {
"force_no_cache": false,
"token_type": "default-service",
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"bar/": map[string]interface{}{
@ -521,9 +561,13 @@ func TestSysRemountAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"token/": map[string]interface{}{
"description": "token based credentials",
@ -535,9 +579,13 @@ func TestSysRemountAuth(t *testing.T) {
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)

View File

@ -39,9 +39,13 @@ func TestSysMounts(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -53,9 +57,13 @@ func TestSysMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -66,9 +74,13 @@ func TestSysMounts(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -80,9 +92,13 @@ func TestSysMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"secret/": map[string]interface{}{
@ -94,9 +110,13 @@ func TestSysMounts(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -108,9 +128,13 @@ func TestSysMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -121,9 +145,13 @@ func TestSysMounts(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -135,9 +163,13 @@ func TestSysMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -196,9 +228,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -209,9 +245,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -223,9 +263,13 @@ func TestSysMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -236,9 +280,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -250,9 +298,13 @@ func TestSysMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"foo/": map[string]interface{}{
@ -264,9 +316,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -277,9 +333,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -291,9 +351,13 @@ func TestSysMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -304,9 +368,13 @@ func TestSysMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -318,9 +386,13 @@ func TestSysMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -414,9 +486,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -427,9 +503,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -441,9 +521,13 @@ func TestSysRemount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -454,9 +538,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -468,9 +556,13 @@ func TestSysRemount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"bar/": map[string]interface{}{
@ -482,9 +574,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -495,9 +591,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -509,9 +609,13 @@ func TestSysRemount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -522,9 +626,13 @@ func TestSysRemount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -536,9 +644,13 @@ func TestSysRemount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -597,9 +709,13 @@ func TestSysUnmount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -611,9 +727,13 @@ func TestSysUnmount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -624,9 +744,13 @@ func TestSysUnmount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -638,9 +762,13 @@ func TestSysUnmount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"secret/": map[string]interface{}{
@ -652,9 +780,13 @@ func TestSysUnmount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -666,9 +798,13 @@ func TestSysUnmount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -679,9 +815,13 @@ func TestSysUnmount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -693,9 +833,13 @@ func TestSysUnmount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -840,9 +984,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -853,9 +1001,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -867,9 +1019,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -880,9 +1036,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -894,9 +1054,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"foo/": map[string]interface{}{
@ -908,9 +1072,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -921,9 +1089,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -935,9 +1107,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -948,9 +1124,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -962,9 +1142,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}
testResponseStatus(t, resp, 200)
@ -1049,9 +1233,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -1062,9 +1250,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -1076,9 +1268,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -1089,9 +1285,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -1103,9 +1303,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
},
"foo/": map[string]interface{}{
@ -1117,9 +1321,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"secret/": map[string]interface{}{
"description": "key/value secret storage",
@ -1130,9 +1338,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"local": false,
"seal_wrap": false,
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"sys/": map[string]interface{}{
"description": "system endpoints used for control, policy and debugging",
@ -1144,9 +1356,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"local": false,
"seal_wrap": true,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -1157,9 +1373,13 @@ func TestSysTuneMount(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"local": true,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -1171,9 +1391,13 @@ func TestSysTuneMount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []interface{}{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"local": false,
"seal_wrap": false,
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"version": "",
},
}

View File

@ -5,7 +5,7 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/go-version"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/wrapping"
@ -15,7 +15,8 @@ import (
// Looker defines the plugin Lookup function that looks into the plugin catalog
// for available plugins and returns a PluginRunner
type Looker interface {
LookupPlugin(context.Context, string, consts.PluginType) (*PluginRunner, error)
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*PluginRunner, error)
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*PluginRunner, error)
}
// RunnerUtil interface defines the functions needed by the runner to wrap the

View File

@ -54,7 +54,11 @@ type SystemView interface {
// LookupPlugin looks into the plugin catalog for a plugin with the given
// name. Returns a PluginRunner or an error if a plugin can not be found.
LookupPlugin(context.Context, string, consts.PluginType) (*pluginutil.PluginRunner, error)
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error)
// LookupPluginVersion looks into the plugin catalog for a plugin with the given
// name and version. Returns a PluginRunner or an error if a plugin can not be found.
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*pluginutil.PluginRunner, error)
// NewPluginClient returns a client for managing the lifecycle of plugin
// processes
@ -168,6 +172,10 @@ func (d StaticSystemView) LookupPlugin(_ context.Context, _ string, _ consts.Plu
return nil, errors.New("LookupPlugin is not implemented in StaticSystemView")
}
func (d StaticSystemView) LookupPluginVersion(_ context.Context, _ string, _ consts.PluginType, _ string) (*pluginutil.PluginRunner, error) {
return nil, errors.New("LookupPluginVersion is not implemented in StaticSystemView")
}
func (d StaticSystemView) MlockEnabled() bool {
return d.EnableMlock
}

View File

@ -107,6 +107,10 @@ func (s *gRPCSystemViewClient) LookupPlugin(_ context.Context, _ string, _ const
return nil, fmt.Errorf("cannot call LookupPlugin from a plugin backend")
}
func (s *gRPCSystemViewClient) LookupPluginVersion(_ context.Context, _ string, _ consts.PluginType, _ string) (*pluginutil.PluginRunner, error) {
return nil, fmt.Errorf("cannot call LookupPluginVersion from a plugin backend")
}
func (s *gRPCSystemViewClient) MlockEnabled() bool {
reply, err := s.client.MlockEnabled(context.Background(), &pb.Empty{})
if err != nil {

View File

@ -6,7 +6,7 @@ import (
"fmt"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
@ -28,13 +28,13 @@ func (b *BackendPluginClient) Cleanup(ctx context.Context) {
b.client.Kill()
}
// NewBackend will return an instance of an RPC-based client implementation of the backend for
// NewBackendWithVersion will return an instance of an RPC-based client implementation of the backend for
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
// the plugin should run in metadata mode.
func NewBackend(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool) (logical.Backend, error) {
func NewBackendWithVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool, version string) (logical.Backend, error) {
// Look for plugin in the plugin catalog
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, pluginType)
pluginRunner, err := sys.LookupPluginVersion(ctx, pluginName, pluginType, version)
if err != nil {
return nil, err
}
@ -66,6 +66,14 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
return backend, nil
}
// NewBackend will return an instance of an RPC-based client implementation of the backend for
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
// the plugin should run in metadata mode.
func NewBackend(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool) (logical.Backend, error) {
return NewBackendWithVersion(ctx, pluginName, pluginType, sys, conf, isMetadataMode, "")
}
func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (logical.Backend, error) {
// pluginMap is the map of plugins we can dispense.
pluginSet := map[int]plugin.PluginSet{

View File

@ -911,7 +911,7 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV
f, ok := c.credentialBackends[t]
if !ok {
plug, err := c.pluginCatalog.Get(ctx, t, consts.PluginTypeCredential, "")
plug, err := c.pluginCatalog.Get(ctx, t, consts.PluginTypeCredential, entry.Version)
if err != nil {
return nil, err
}
@ -938,6 +938,7 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV
}
conf["plugin_type"] = consts.PluginTypeCredential.String()
conf["plugin_version"] = entry.Version
authLogger := c.baseLogger.Named(fmt.Sprintf("auth.%s.%s", t, entry.Accessor))
c.AddLogger(authLogger)

View File

@ -0,0 +1,318 @@
package vault
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"sync"
"testing"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/framework"
)
var (
compileOnce sync.Once
pluginBytes []byte
)
func testCoreWithPlugin(t *testing.T) (*Core, string, string) {
t.Helper()
pluginName, pluginSha256, pluginDir := compilePlugin(t)
conf := &CoreConfig{
BuiltinRegistry: NewMockBuiltinRegistry(),
PluginDirectory: pluginDir,
}
core := TestCoreWithSealAndUI(t, conf)
core, _, _ = testCoreUnsealed(t, core)
return core, pluginName, pluginSha256
}
// to mount a plugin, we need a working binary plugin, so we compile one here.
func compilePlugin(t *testing.T) (string, string, string) {
pluginType := "approle"
pluginName := "vault-plugin-auth-" + pluginType
dir := ""
// detect if we are in the "vault/" or the root directory and compensate
if _, err := os.Stat("builtin"); os.IsNotExist(err) {
wd, err := os.Getwd()
if err != nil {
panic(err)
}
dir = filepath.Dir(wd)
}
pluginDir, cleanup := MakeTestPluginDir(t)
t.Cleanup(func() { cleanup(t) })
pluginPath := path.Join(pluginDir, pluginName)
// cache the compilation to only run once
compileOnce.Do(func() {
cmd := exec.Command("go", "build", "-o", pluginPath, fmt.Sprintf("builtin/credential/%s/cmd/%s/main.go", pluginType, pluginType))
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Errorf("error running go build %v output: %s", err, output))
}
pluginBytes, err = os.ReadFile(pluginPath)
if err != nil {
panic(err)
}
})
// write the cached plugin if necessary
var err error
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
err = os.WriteFile(pluginPath, pluginBytes, 0o777)
}
if err != nil {
t.Fatal(err)
}
sha := sha256.New()
_, err = sha.Write(pluginBytes)
if err != nil {
t.Fatal(err)
}
return pluginName, fmt.Sprintf("%x", sha.Sum(nil)), pluginDir
}
func TestCore_EnableExternalCredentialPlugin(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"version": "v1.0.0",
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: pluginName,
Version: "v1.0.0",
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "auth/foo/" {
t.Fatalf("missing mount, match: %q", match)
}
}
func TestCore_EnableExternalCredentialPlugin_MultipleVersions(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"version": "v1.0.0",
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
d = &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"version": "v1.0.1",
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err = c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: pluginName,
Version: "v1.0.0",
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "auth/foo/" {
t.Fatalf("missing mount, match: %q", match)
}
raw, _ := c.router.root.Get(match)
if raw.(*routeEntry).mountEntry.Version != "v1.0.0" {
t.Errorf("Expected mount to be version v1.0.0 but got %s", raw.(*routeEntry).mountEntry.Version)
}
}
func TestCore_EnableExternalCredentialPlugin_MultipleVersions_MountSecond(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"command": pluginName,
"version": "v1.0.0",
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
d = &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"version": "v1.0.1",
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err = c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: pluginName,
Version: "v1.0.1",
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "auth/foo/" {
t.Fatalf("missing mount, match: %q", match)
}
raw, _ := c.router.root.Get(match)
if raw.(*routeEntry).mountEntry.Version != "v1.0.1" {
t.Errorf("Expected mount to be version v1.0.1 but got %s", raw.(*routeEntry).mountEntry.Version)
}
}
func TestCore_EnableExternalCredentialPlugin_NoVersionsOkay(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: pluginName,
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "auth/foo/" {
t.Fatalf("missing mount, match: %q", match)
}
}
func TestCore_EnableExternalCredentialPlugin_NoVersionOnRegister(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: pluginName,
Version: "v1.0.0",
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err == nil || !errors.Is(err, ErrPluginNotFound) {
t.Fatalf("Expected to get plugin not found but got: %v", err)
}
}
func TestCore_EnableExternalCredentialPlugin_InvalidName(t *testing.T) {
c, pluginName, pluginSha256 := testCoreWithPlugin(t)
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSha256,
"version": "v1.0.0",
"command": pluginName + "xyz",
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
_, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err == nil || !strings.Contains(err.Error(), "no such file or directory") {
t.Fatalf("should have gotten a no such file or directory error inserting the plugin: %v", err)
}
}

View File

@ -7,7 +7,7 @@ import (
"testing"
"time"
metrics "github.com/armon/go-metrics"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/helper/jsonutil"

View File

@ -234,13 +234,19 @@ func (d dynamicSystemView) NewPluginClient(ctx context.Context, config pluginuti
// LookupPlugin looks for a plugin with the given name in the plugin catalog. It
// returns a PluginRunner or an error if no plugin was found.
func (d dynamicSystemView) LookupPlugin(ctx context.Context, name string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error) {
return d.LookupPluginVersion(ctx, name, pluginType, "")
}
// LookupPluginVersion looks for a plugin with the given name and version in the plugin catalog. It
// returns a PluginRunner or an error if no plugin was found.
func (d dynamicSystemView) LookupPluginVersion(ctx context.Context, name string, pluginType consts.PluginType, version string) (*pluginutil.PluginRunner, error) {
if d.core == nil {
return nil, fmt.Errorf("system view core is nil")
}
if d.core.pluginCatalog == nil {
return nil, fmt.Errorf("system view core plugin catalog is nil")
}
r, err := d.core.pluginCatalog.Get(ctx, name, pluginType, "")
r, err := d.core.pluginCatalog.Get(ctx, name, pluginType, version)
if err != nil {
return nil, err
}

View File

@ -620,7 +620,8 @@ func getVersion(d *framework.FieldData) (string, error) {
}
// Canonicalize the version string.
version = semanticVersion.String()
// Add the 'v' back in, since semantic version strips it out, and we want to be consistent with internal plugins.
version = "v" + semanticVersion.String()
}
return version, nil
@ -888,6 +889,10 @@ func mountInfo(entry *MountEntry) map[string]interface{} {
"external_entropy_access": entry.ExternalEntropyAccess,
"options": entry.Options,
"uuid": entry.UUID,
"version": entry.Version,
"sha": entry.Sha,
"running_version": entry.RunningVersion,
"running_sha": entry.RunningSha,
}
entryConfig := map[string]interface{}{
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
@ -981,6 +986,14 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d
sealWrap := data.Get("seal_wrap").(bool)
externalEntropyAccess := data.Get("external_entropy_access").(bool)
options := data.Get("options").(map[string]string)
version := data.Get("version").(string)
if version != "" {
v, err := semver.NewSemver(version)
if err != nil {
return nil, err
}
version = "v" + v.String()
}
var config MountConfig
var apiConfig APIMountConfig
@ -1123,6 +1136,7 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d
SealWrap: sealWrap,
ExternalEntropyAccess: externalEntropyAccess,
Options: options,
Version: version,
}
// Attempt mount
@ -2223,6 +2237,14 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
sealWrap := data.Get("seal_wrap").(bool)
externalEntropyAccess := data.Get("external_entropy_access").(bool)
options := data.Get("options").(map[string]string)
version := data.Get("version").(string)
if version != "" {
v, err := semver.NewSemver(version)
if err != nil {
return nil, err
}
version = "v" + v.String()
}
var config MountConfig
var apiConfig APIMountConfig
@ -2354,6 +2376,7 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
SealWrap: sealWrap,
ExternalEntropyAccess: externalEntropyAccess,
Options: options,
Version: version,
}
// Attempt enabling

View File

@ -1600,6 +1600,10 @@ func (b *SystemBackend) authPaths() []*framework.Path {
Type: framework.TypeKVPairs,
Description: strings.TrimSpace(sysHelp["auth_options"][0]),
},
"version": {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["plugin-catalog_version"][0]),
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
@ -1971,6 +1975,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path {
Type: framework.TypeKVPairs,
Description: strings.TrimSpace(sysHelp["mount_options"][0]),
},
"version": {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["plugin-catalog_version"][0]),
},
},
Operations: map[logical.Operation]framework.OperationHandler{

View File

@ -17,7 +17,7 @@ import (
"github.com/fatih/structs"
"github.com/go-test/deep"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-hclog"
semver "github.com/hashicorp/go-version"
"github.com/hashicorp/vault/audit"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
@ -172,6 +172,10 @@ func TestSystemBackend_mounts(t *testing.T) {
"options": map[string]string{
"version": "1",
},
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"sys/": map[string]interface{}{
"type": "system",
@ -185,9 +189,13 @@ func TestSystemBackend_mounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -200,9 +208,13 @@ func TestSystemBackend_mounts(t *testing.T) {
"max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -216,9 +228,13 @@ func TestSystemBackend_mounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
}
if diff := deep.Equal(resp.Data, exp); len(diff) > 0 {
@ -285,6 +301,10 @@ func TestSystemBackend_mount(t *testing.T) {
"options": map[string]string{
"version": "1",
},
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"sys/": map[string]interface{}{
"type": "system",
@ -298,9 +318,13 @@ func TestSystemBackend_mount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -313,9 +337,13 @@ func TestSystemBackend_mount(t *testing.T) {
"max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -329,9 +357,13 @@ func TestSystemBackend_mount(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"prod/secret/": map[string]interface{}{
"description": "",
@ -349,6 +381,10 @@ func TestSystemBackend_mount(t *testing.T) {
"options": map[string]string{
"version": "1",
},
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
}
if diff := deep.Equal(resp.Data, exp); len(diff) > 0 {
@ -1818,9 +1854,13 @@ func TestSystemBackend_authTable(t *testing.T) {
"force_no_cache": false,
"token_type": "default-service",
},
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
}
if diff := deep.Equal(resp.Data, exp); diff != nil {
@ -1882,9 +1922,13 @@ func TestSystemBackend_enableAuth(t *testing.T) {
"force_no_cache": false,
"token_type": "default-service",
},
"local": true,
"seal_wrap": true,
"options": map[string]string{},
"local": true,
"seal_wrap": true,
"options": map[string]string{},
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"token/": map[string]interface{}{
"type": "token",
@ -1898,9 +1942,13 @@ func TestSystemBackend_enableAuth(t *testing.T) {
"force_no_cache": false,
"token_type": "default-service",
},
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
}
if diff := deep.Equal(resp.Data, exp); diff != nil {
@ -2893,21 +2941,25 @@ func TestSystemBackend_rotate(t *testing.T) {
}
func testSystemBackend(t *testing.T) logical.Backend {
t.Helper()
c, _, _ := TestCoreUnsealed(t)
return c.systemBackend
}
func testSystemBackendRaw(t *testing.T) logical.Backend {
t.Helper()
c, _, _ := TestCoreUnsealedRaw(t)
return c.systemBackend
}
func testCoreSystemBackend(t *testing.T) (*Core, logical.Backend, string) {
t.Helper()
c, _, root := TestCoreUnsealed(t)
return c, c.systemBackend, root
}
func testCoreSystemBackendRaw(t *testing.T) (*Core, logical.Backend, string) {
t.Helper()
c, _, root := TestCoreUnsealedRaw(t)
return c, c.systemBackend, root
}
@ -3033,7 +3085,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
"args": []string{"--test"},
"sha256": "31",
"builtin": false,
"version": "0.1.0",
"version": "v0.1.0",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actual, expected)
@ -3270,6 +3322,10 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
"options": map[string]string{
"version": "1",
},
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"sys/": map[string]interface{}{
"type": "system",
@ -3283,9 +3339,13 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Accept"},
},
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"local": false,
"seal_wrap": true,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage",
@ -3298,9 +3358,13 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
"max_lease_ttl": resp.Data["secret"].(map[string]interface{})["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"local": true,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
"identity/": map[string]interface{}{
"description": "identity store",
@ -3314,9 +3378,13 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
"force_no_cache": false,
"passthrough_request_headers": []string{"Authorization"},
},
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"local": false,
"seal_wrap": false,
"options": map[string]string(nil),
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
},
"auth": map[string]interface{}{
@ -3335,6 +3403,10 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
"uuid": resp.Data["auth"].(map[string]interface{})["token/"].(map[string]interface{})["uuid"],
"local": false,
"seal_wrap": false,
"sha": "",
"running_sha": "",
"version": "",
"running_version": "",
},
},
}

View File

@ -12,7 +12,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/go-secure-stdlib/strutil"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/builtin/plugin"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/helper/namespace"
@ -328,6 +328,12 @@ type MountEntry struct {
// without separately managing their locks individually. See SyncCache() for
// the specific values that are being cached.
synthesizedConfigCache sync.Map
// version info
Version string `json:"version,omitempty"`
Sha string `json:"sha,omitempty"`
RunningVersion string `json:"running_version,omitempty"`
RunningSha string `json:"running_sha,omitempty"`
}
// MountConfig is used to hold settable options
@ -1419,7 +1425,7 @@ func (c *Core) newLogicalBackend(ctx context.Context, entry *MountEntry, sysView
f, ok := c.logicalBackends[t]
if !ok {
plug, err := c.pluginCatalog.Get(ctx, t, consts.PluginTypeSecrets, "")
plug, err := c.pluginCatalog.Get(ctx, t, consts.PluginTypeSecrets, entry.Version)
if err != nil {
return nil, err
}

View File

@ -2424,3 +2424,37 @@ func CreateTestClusterWithRollbackPeriod(t testing.T, newPeriod time.Duration, b
// Return the cluster.
return cluster
}
// MakeTestPluginDir creates a temporary directory suitable for holding plugins.
// This helper also resolves symlinks to make tests happy on OS X.
func MakeTestPluginDir(t testing.T) (string, func(t testing.T)) {
if t != nil {
t.Helper()
}
dir, err := os.MkdirTemp("", "")
if err != nil {
if t == nil {
panic(err)
}
t.Fatal(err)
}
// OSX tempdir are /var, but actually symlinked to /private/var
dir, err = filepath.EvalSymlinks(dir)
if err != nil {
if t == nil {
panic(err)
}
t.Fatal(err)
}
return dir, func(t testing.T) {
if err := os.RemoveAll(dir); err != nil {
if t == nil {
panic(err)
}
t.Fatal(err)
}
}
}