Skip to content

Commit

Permalink
Make the plugin catalog endpoint roundtrip so we can use terraform to…
Browse files Browse the repository at this point in the history
… manage them. (#3778)
  • Loading branch information
tomwilkie authored and jefferai committed Jan 18, 2018
1 parent bb45c06 commit e884946
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 35 deletions.
41 changes: 37 additions & 4 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"errors"
"fmt"
"hash"
"path/filepath"
"strconv"
"strings"
"sync"
"time"

"github.com/fatih/structs"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/parseutil"
Expand Down Expand Up @@ -860,6 +860,10 @@ func NewSystemBackend(core *Core) *SystemBackend {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["plugin-catalog_command"][0]),
},
"args": &framework.FieldSchema{
Type: framework.TypeStringSlice,
Description: strings.TrimSpace(sysHelp["plugin-catalog_args"][0]),
},
},

Callbacks: map[logical.Operation]framework.OperationFunc{
Expand Down Expand Up @@ -1098,12 +1102,24 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
return logical.ErrorResponse("missing command value"), nil
}

// For backwards compatibility, also accept args as part of command. Don't
// accepts args in both command and args.
args := d.Get("args").([]string)
parts := strings.Split(command, " ")
if len(parts) <= 0 {
return logical.ErrorResponse("missing command value"), nil
} else if len(parts) > 1 && len(args) > 0 {
return logical.ErrorResponse("must not speficy args in command and args field"), nil
} else if len(parts) > 1 {
args = parts[1:]
}

sha256Bytes, err := hex.DecodeString(sha256)
if err != nil {
return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err
}

err = b.Core.pluginCatalog.Set(pluginName, command, sha256Bytes)
err = b.Core.pluginCatalog.Set(pluginName, parts[0], args, sha256Bytes)
if err != nil {
return nil, err
}
Expand All @@ -1124,8 +1140,21 @@ func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, req *logica
return nil, nil
}

// Create a map of data to be returned and remove sensitive information from it
data := structs.New(plugin).Map()
command := ""
if !plugin.Builtin {
command, err = filepath.Rel(b.Core.pluginCatalog.directory, plugin.Command)
if err != nil {
return nil, err
}
}

data := map[string]interface{}{
"name": plugin.Name,
"args": plugin.Args,
"command": command,
"sha256": hex.EncodeToString(plugin.Sha256),
"builtin": plugin.Builtin,
}

return &logical.Response{
Data: data,
Expand Down Expand Up @@ -3332,6 +3361,10 @@ executable defined in this command must exist in vault's
plugin directory.`,
"",
},
"plugin-catalog_args": {
`The args passed to plugin command.`,
"",
},
"leases": {
`View or list lease metadata.`,
`
Expand Down
41 changes: 25 additions & 16 deletions vault/logical_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/fatih/structs"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/helper/builtinplugins"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/helper/salt"
"github.com/hashicorp/vault/logical"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -1787,14 +1786,15 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
if err != nil {
t.Fatalf("err: %v", err)
}
actualRespData := resp.Data

expectedBuiltin := &pluginutil.PluginRunner{
Name: "mysql-database-plugin",
Builtin: true,
actualRespData := resp.Data
expectedRespData := map[string]interface{}{
"name": "mysql-database-plugin",
"command": "",
"args": []string(nil),
"sha256": "",
"builtin": true,
}
expectedRespData := structs.New(expectedBuiltin).Map()

if !reflect.DeepEqual(actualRespData, expectedRespData) {
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actualRespData, expectedRespData)
}
Expand All @@ -1806,31 +1806,40 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
}
defer file.Close()

// Check we can only specify args in one of command or args.
command := fmt.Sprintf("%s --test", filepath.Base(file.Name()))
req = logical.TestRequest(t, logical.UpdateOperation, "plugins/catalog/test-plugin")
req.Data["args"] = []string{"--foo"}
req.Data["sha_256"] = hex.EncodeToString([]byte{'1'})
req.Data["command"] = command
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp.Error().Error() != "must not speficy args in command and args field" {
t.Fatalf("err: %v", resp.Error())
}

delete(req.Data, "args")
resp, err = b.HandleRequest(context.Background(), req)
if err != nil || resp.Error() != nil {
t.Fatalf("err: %v %v", err, resp.Error())
}

req = logical.TestRequest(t, logical.ReadOperation, "plugins/catalog/test-plugin")
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("err: %v", err)
}
actual := resp.Data

expectedRunner := &pluginutil.PluginRunner{
Name: "test-plugin",
Command: filepath.Join(sym, filepath.Base(file.Name())),
Args: []string{"--test"},
Sha256: []byte{'1'},
Builtin: false,
actual := resp.Data
expected := map[string]interface{}{
"name": "test-plugin",
"command": filepath.Base(file.Name()),
"args": []string{"--test"},
"sha256": "31",
"builtin": false,
}
expected := structs.New(expectedRunner).Map()

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actual, expected)
}
Expand Down
10 changes: 4 additions & 6 deletions vault/plugin_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (c *PluginCatalog) Get(name string) (*pluginutil.PluginRunner, error) {

// Set registers a new external plugin with the catalog, or updates an existing
// external plugin. It takes the name, command and SHA256 of the plugin.
func (c *PluginCatalog) Set(name, command string, sha256 []byte) error {
func (c *PluginCatalog) Set(name, command string, args []string, sha256 []byte) error {
if c.directory == "" {
return ErrDirectoryNotConfigured
}
Expand All @@ -100,11 +100,9 @@ func (c *PluginCatalog) Set(name, command string, sha256 []byte) error {
c.lock.Lock()
defer c.lock.Unlock()

parts := strings.Split(command, " ")

// Best effort check to make sure the command isn't breaking out of the
// configured plugin directory.
commandFull := filepath.Join(c.directory, parts[0])
commandFull := filepath.Join(c.directory, command)
sym, err := filepath.EvalSymlinks(commandFull)
if err != nil {
return fmt.Errorf("error while validating the command path: %v", err)
Expand All @@ -120,8 +118,8 @@ func (c *PluginCatalog) Set(name, command string, sha256 []byte) error {

entry := &pluginutil.PluginRunner{
Name: name,
Command: parts[0],
Args: parts[1:],
Command: command,
Args: args,
Sha256: sha256,
Builtin: false,
}
Expand Down
10 changes: 5 additions & 5 deletions vault/plugin_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ func TestPluginCatalog_CRUD(t *testing.T) {
}
defer file.Close()

command := fmt.Sprintf("%s --test", filepath.Base(file.Name()))
err = core.pluginCatalog.Set("mysql-database-plugin", command, []byte{'1'})
command := fmt.Sprintf("%s", filepath.Base(file.Name()))
err = core.pluginCatalog.Set("mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -139,14 +139,14 @@ func TestPluginCatalog_List(t *testing.T) {
}
defer file.Close()

command := fmt.Sprintf("%s --test", filepath.Base(file.Name()))
err = core.pluginCatalog.Set("mysql-database-plugin", command, []byte{'1'})
command := filepath.Base(file.Name())
err = core.pluginCatalog.Set("mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
if err != nil {
t.Fatal(err)
}

// Set another plugin
err = core.pluginCatalog.Set("aaaaaaa", command, []byte{'1'})
err = core.pluginCatalog.Set("aaaaaaa", command, []string{"--test"}, []byte{'1'})
if err != nil {
t.Fatal(err)
}
Expand Down
10 changes: 6 additions & 4 deletions vault/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,9 @@ func TestAddTestPlugin(t testing.T, c *Core, name, testFunc string) {
c.pluginDirectory = directoryPath
c.pluginCatalog.directory = directoryPath

command := fmt.Sprintf("%s --test.run=%s", filepath.Base(os.Args[0]), testFunc)
err = c.pluginCatalog.Set(name, command, sum)
command := fmt.Sprintf("%s", filepath.Base(os.Args[0]))
args := []string{fmt.Sprintf("--test.run=%s", testFunc)}
err = c.pluginCatalog.Set(name, command, args, sum)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -472,8 +473,9 @@ func TestAddTestPluginTempDir(t testing.T, c *Core, name, testFunc, tempDir stri
c.pluginDirectory = fullPath
c.pluginCatalog.directory = fullPath

command := fmt.Sprintf("%s --test.run=%s", filepath.Base(os.Args[0]), testFunc)
err = c.pluginCatalog.Set(name, command, sum)
command := fmt.Sprintf("%s", filepath.Base(os.Args[0]))
args := []string{fmt.Sprintf("--test.run=%s", testFunc)}
err = c.pluginCatalog.Set(name, command, args, sum)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit e884946

Please sign in to comment.