Skip to content

Commit

Permalink
Update mount table and CLI with plugin version for auth (#16856)
Browse files Browse the repository at this point in the history
  • Loading branch information
swenson committed Aug 31, 2022
1 parent 5e44064 commit 9d97dec
Show file tree
Hide file tree
Showing 32 changed files with 1,479 additions and 328 deletions.
5 changes: 5 additions & 0 deletions api/sys_mounts.go
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down
153 changes: 153 additions & 0 deletions api/sys_mounts_test.go
@@ -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": ""
}
}
}`
24 changes: 23 additions & 1 deletion api/sys_plugins.go
Expand Up @@ -22,13 +22,22 @@ 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:
// "types") instead.
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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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()
Expand Down
29 changes: 27 additions & 2 deletions api/sys_plugins_test.go
Expand Up @@ -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()
Expand Down Expand Up @@ -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))
}

Expand Down Expand Up @@ -77,3 +96,9 @@ const listUntypedResponse = `{
"warnings": null,
"auth": null
}`

func mockVaultHandlerRegister(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(registerResponse))
}

const registerResponse = `{}`
7 changes: 4 additions & 3 deletions builtin/plugin/backend.go
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
10 changes: 10 additions & 0 deletions builtin/plugin/backend_lazyLoad_test.go
Expand Up @@ -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 changes: 3 additions & 0 deletions changelog/16856.txt
@@ -0,0 +1,3 @@
```release-note:change
plugins: Add plugin version to auth register, list, and mount table
```
9 changes: 9 additions & 0 deletions command/auth_enable.go
Expand Up @@ -37,6 +37,7 @@ type AuthEnableCommand struct {
flagExternalEntropyAccess bool
flagTokenType string
flagVersion int
flagPluginVersion string
}

func (c *AuthEnableCommand) Synopsis() string {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 9d97dec

Please sign in to comment.