vault: convert system to logical.Backend
This commit is contained in:
parent
c3ae1b59a1
commit
5ffcd02b7a
|
@ -0,0 +1,142 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// SystemBackend implements logical.Backend and is used to interact with
|
||||
// the core of the system. This backend is hardcoded to exist at the "sys"
|
||||
// prefix. Conceptually it is similar to procfs on Linux.
|
||||
type SystemBackend2 struct {
|
||||
Core *Core
|
||||
}
|
||||
|
||||
func (b *SystemBackend2) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||
// Switch on the path to route to the appropriate handler
|
||||
switch {
|
||||
case req.Path == "mounts":
|
||||
return b.handleMountTable(req)
|
||||
case strings.HasPrefix(req.Path, "mount/"):
|
||||
return b.handleMountOperation(req)
|
||||
case req.Path == "remount":
|
||||
return b.handleRemount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedPath
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SystemBackend2) RootPaths() []string {
|
||||
return []string{
|
||||
"mount/*",
|
||||
"remount",
|
||||
}
|
||||
}
|
||||
|
||||
// handleMountTable handles the "mounts" endpoint to provide the mount table
|
||||
func (b *SystemBackend2) handleMountTable(req *logical.Request) (*logical.Response, error) {
|
||||
switch req.Operation {
|
||||
case logical.ReadOperation:
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
b.Core.mountsLock.RLock()
|
||||
defer b.Core.mountsLock.RUnlock()
|
||||
|
||||
resp := &logical.Response{
|
||||
IsSecret: false,
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
for _, entry := range b.Core.mounts.Entries {
|
||||
info := map[string]string{
|
||||
"type": entry.Type,
|
||||
"description": entry.Description,
|
||||
}
|
||||
resp.Data[entry.Path] = info
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// handleMountOperation is used to mount or unmount a path
|
||||
func (b *SystemBackend2) handleMountOperation(req *logical.Request) (*logical.Response, error) {
|
||||
switch req.Operation {
|
||||
case logical.WriteOperation:
|
||||
return b.handleMount(req)
|
||||
case logical.DeleteOperation:
|
||||
return b.handleUnmount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
}
|
||||
|
||||
// handleMount is used to mount a new path
|
||||
func (b *SystemBackend2) handleMount(req *logical.Request) (*logical.Response, error) {
|
||||
suffix := strings.TrimPrefix(req.Path, "mount/")
|
||||
if len(suffix) == 0 {
|
||||
return logical.ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Get the type and description (optionally)
|
||||
logicalType := req.GetString("type")
|
||||
if logicalType == "" {
|
||||
return logical.ErrorResponse("backend type must be specified as a string"), ErrInvalidRequest
|
||||
}
|
||||
description := req.GetString("description")
|
||||
|
||||
// Create the mount entry
|
||||
me := &MountEntry{
|
||||
Path: suffix,
|
||||
Type: logicalType,
|
||||
Description: description,
|
||||
}
|
||||
|
||||
// Attempt mount
|
||||
if err := b.Core.mount(me); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleUnmount is used to unmount a path
|
||||
func (b *SystemBackend2) handleUnmount(req *logical.Request) (*logical.Response, error) {
|
||||
suffix := strings.TrimPrefix(req.Path, "mount/")
|
||||
if len(suffix) == 0 {
|
||||
return logical.ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt unmount
|
||||
if err := b.Core.unmount(suffix); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleRemount is used to remount a path
|
||||
func (b *SystemBackend2) handleRemount(req *logical.Request) (*logical.Response, error) {
|
||||
// Only accept write operations
|
||||
switch req.Operation {
|
||||
case WriteOperation:
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// Get the paths
|
||||
fromPath := req.GetString("from")
|
||||
toPath := req.GetString("to")
|
||||
if fromPath == "" || toPath == "" {
|
||||
return logical.ErrorResponse(
|
||||
"both 'from' and 'to' path must be specified as a string"),
|
||||
ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt remount
|
||||
if err := b.Core.remount(fromPath, toPath); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
func TestSystemBackend_impl(t *testing.T) {
|
||||
var _ logical.Backend = new(SystemBackend2)
|
||||
}
|
||||
|
||||
func TestSystemBackend_RootPaths(t *testing.T) {
|
||||
expected := []string{
|
||||
"mount/*",
|
||||
"remount",
|
||||
}
|
||||
|
||||
b := testSystemBackend(t)
|
||||
actual := b.RootPaths()
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_mounts(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
req := logical.TestRequest(t, logical.ReadOperation, "mounts")
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
exp := map[string]interface{}{
|
||||
"secret/": map[string]string{
|
||||
"type": "generic",
|
||||
"description": "generic secret storage",
|
||||
},
|
||||
"sys/": map[string]string{
|
||||
"type": "system",
|
||||
"description": "system endpoints used for control, policy and debugging",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Data, exp) {
|
||||
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_mount(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "mount/prod/secret/")
|
||||
req.Data["type"] = "generic"
|
||||
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_mount_invalid(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "mount/prod/secret/")
|
||||
req.Data["type"] = "nope"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "unknown logical backend type: nope" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_unmount(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.DeleteOperation, "mount/secret/")
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_unmount_invalid(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.DeleteOperation, "mount/foo/")
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_remount(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "remount")
|
||||
req.Data["from"] = "secret"
|
||||
req.Data["to"] = "foo"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_remount_invalid(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "remount")
|
||||
req.Data["from"] = "unknown"
|
||||
req.Data["to"] = "foo"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount at 'unknown/'" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_remount_system(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "remount")
|
||||
req.Data["from"] = "sys"
|
||||
req.Data["to"] = "foo"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "cannot remount 'sys/'" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func testSystemBackend(t *testing.T) *SystemBackend2 {
|
||||
c, _ := TestCoreUnsealed(t)
|
||||
return &SystemBackend2{Core: c}
|
||||
}
|
Loading…
Reference in New Issue