vault: system using the framework

This commit is contained in:
Mitchell Hashimoto 2015-03-15 17:35:59 -07:00
parent 2d92c2ee10
commit 1be431df51
4 changed files with 133 additions and 67 deletions

View File

@ -24,6 +24,12 @@ type Backend struct {
// backend is in use).
Paths []*Path
// PathsRoot is the list of path patterns that denote the
// paths above that require root-level privileges. These can't be
// regular expressions, it is either exact match or prefix match.
// For prefix match, append '*' as a suffix.
PathsRoot []string
once sync.Once
pathsRe []*regexp.Regexp
}
@ -46,12 +52,6 @@ type Path struct {
// whereas all fields are avaiable in the Write operation.
Fields map[string]*FieldSchema
// Root if not blank, denotes that this path requires root
// privileges and the path pattern that is the root path. This can't
// be a regular expression and must be an exact path. It may have a
// trailing '*' to denote that it is a prefix, and not an exact match.
Root string
// Callbacks are the set of callbacks that are called for a given
// operation. If a callback for a specific operation is not present,
// then logical.ErrUnsupportedOperation is automatically generated.
@ -123,8 +123,7 @@ func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error)
// logical.Backend impl.
func (b *Backend) RootPaths() []string {
// TODO
return nil
return b.PathsRoot
}
// Route looks up the path that would be used for a given path string.

View File

@ -158,7 +158,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
}
backends["generic"] = PassthroughBackendFactory
backends["system"] = func(map[string]string) (logical.Backend, error) {
return &SystemBackend{Core: c}, nil
return NewSystemBackend(c), nil
}
c.backends = backends

View File

@ -4,8 +4,72 @@ import (
"strings"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
func NewSystemBackend(core *Core) logical.Backend {
b := &SystemBackend{Core: core}
return &framework.Backend{
PathsRoot: []string{
"mount/*",
"remount",
},
Paths: []*framework.Path{
&framework.Path{
Pattern: "mounts",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handleMountTable,
},
HelpSynopsis: strings.TrimSpace(sysHelp["mounts"][0]),
HelpDescription: strings.TrimSpace(sysHelp["mounts"][1]),
},
&framework.Path{
Pattern: "mount/(?P<path>.+?)",
Fields: map[string]*framework.FieldSchema{
"path": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_path"][0]),
},
"type": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_type"][0]),
},
"description": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_desc"][0]),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.WriteOperation: b.handleMount,
logical.DeleteOperation: b.handleUnmount,
},
HelpSynopsis: strings.TrimSpace(sysHelp["mount"][0]),
HelpDescription: strings.TrimSpace(sysHelp["mount"][1]),
},
&framework.Path{
Pattern: "remount",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.WriteOperation: b.handleRemount,
},
HelpSynopsis: strings.TrimSpace(sysHelp["remount"][0]),
HelpDescription: strings.TrimSpace(sysHelp["remount"][1]),
},
},
}
}
// 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.
@ -13,35 +77,9 @@ type SystemBackend struct {
Core *Core
}
func (b *SystemBackend) 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, logical.ErrUnsupportedPath
}
}
func (b *SystemBackend) RootPaths() []string {
return []string{
"mount/*",
"remount",
}
}
// handleMountTable handles the "mounts" endpoint to provide the mount table
func (b *SystemBackend) handleMountTable(req *logical.Request) (*logical.Response, error) {
switch req.Operation {
case logical.ReadOperation:
default:
return nil, logical.ErrUnsupportedOperation
}
func (b *SystemBackend) handleMountTable(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.mountsLock.RLock()
defer b.Core.mountsLock.RUnlock()
@ -60,35 +98,23 @@ func (b *SystemBackend) handleMountTable(req *logical.Request) (*logical.Respons
return resp, nil
}
// handleMountOperation is used to mount or unmount a path
func (b *SystemBackend) 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, logical.ErrUnsupportedOperation
}
}
// handleMount is used to mount a new path
func (b *SystemBackend) handleMount(req *logical.Request) (*logical.Response, error) {
suffix := strings.TrimPrefix(req.Path, "mount/")
if len(suffix) == 0 {
return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest
}
func (b *SystemBackend) handleMount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Get all the options
path := data.Get("path").(string)
logicalType := data.Get("type").(string)
description := data.Get("description").(string)
// Get the type and description (optionally)
logicalType := req.GetString("type")
if logicalType == "" {
return logical.ErrorResponse("backend type must be specified as a string"), logical.ErrInvalidRequest
return logical.ErrorResponse(
"backend type must be specified as a string"),
logical.ErrInvalidRequest
}
description := req.GetString("description")
// Create the mount entry
me := &MountEntry{
Path: suffix,
Path: path,
Type: logicalType,
Description: description,
}
@ -101,7 +127,8 @@ func (b *SystemBackend) handleMount(req *logical.Request) (*logical.Response, er
}
// handleUnmount is used to unmount a path
func (b *SystemBackend) handleUnmount(req *logical.Request) (*logical.Response, error) {
func (b *SystemBackend) handleUnmount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
suffix := strings.TrimPrefix(req.Path, "mount/")
if len(suffix) == 0 {
return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest
@ -116,7 +143,8 @@ func (b *SystemBackend) handleUnmount(req *logical.Request) (*logical.Response,
}
// handleRemount is used to remount a path
func (b *SystemBackend) handleRemount(req *logical.Request) (*logical.Response, error) {
func (b *SystemBackend) handleRemount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Only accept write operations
switch req.Operation {
case logical.WriteOperation:
@ -140,3 +168,46 @@ func (b *SystemBackend) handleRemount(req *logical.Request) (*logical.Response,
return nil, nil
}
// sysHelp is all the help text for the sys backend.
var sysHelp = map[string][2]string{
"mounts": {
"List the currently mounted backends.",
`
List the currently mounted backends: the mount path, the type of the backend,
and a user friendly description of the purpose for the mount.
`,
},
"mount": {
`Mount a new backend at a new path.`,
`
Mount a backend at a new path. A backend can be mounted multiple times at
multiple paths in order to configure multiple separately configured backends.
Example: you might have an AWS backend for the east coast, and one for the
west coast.
`,
},
"mount_path": {
`The path to mount to. Example: "aws/east"`,
"",
},
"mount_type": {
`The type of the backend. Example: "passthrough"`,
"",
},
"mount_desc": {
`User-friendly description for this mount.`,
"",
},
"remount": {
"Move the mount point of an already-mounted backend.",
`
Change the mount point of an already-mounted backend.
`,
},
}

View File

@ -7,10 +7,6 @@ import (
"github.com/hashicorp/vault/logical"
)
func TestSystemBackend_impl(t *testing.T) {
var _ logical.Backend = new(SystemBackend)
}
func TestSystemBackend_RootPaths(t *testing.T) {
expected := []string{
"mount/*",
@ -147,7 +143,7 @@ func TestSystemBackend_remount_system(t *testing.T) {
}
}
func testSystemBackend(t *testing.T) *SystemBackend {
func testSystemBackend(t *testing.T) logical.Backend {
c, _ := TestCoreUnsealed(t)
return &SystemBackend{Core: c}
return NewSystemBackend(c)
}