vault: convert to logical.Request and friends
This commit is contained in:
parent
5ffcd02b7a
commit
d1d1929192
|
@ -1,5 +1,9 @@
|
|||
package logical
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Request is a struct that stores the parameters and context
|
||||
// of a request being made to Vault. It is used to abstract
|
||||
// the details of the higher level request protocol from the handlers.
|
||||
|
@ -47,3 +51,16 @@ const (
|
|||
RevokeOperation = "revoke"
|
||||
HelpOperation = "help"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnsupportedOperation is returned if the operation is not supported
|
||||
// by the logical backend.
|
||||
ErrUnsupportedOperation = errors.New("unsupported operation")
|
||||
|
||||
// ErrUnsupportedPath is returned if the path is not supported
|
||||
// by the logical backend.
|
||||
ErrUnsupportedPath = errors.New("unsupported path")
|
||||
|
||||
// ErrInvalidRequest is returned if the request is invalid
|
||||
ErrInvalidRequest = errors.New("invalid request")
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
"github.com/hashicorp/vault/shamir"
|
||||
)
|
||||
|
@ -149,7 +150,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||
}
|
||||
|
||||
// HandleRequest is used to handle a new incoming request
|
||||
func (c *Core) HandleRequest(req *Request) (*Response, error) {
|
||||
func (c *Core) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
||||
|
@ -272,8 +273,8 @@ func TestCore_Route_Sealed(t *testing.T) {
|
|||
}
|
||||
|
||||
// Should not route anything
|
||||
req := &Request{
|
||||
Operation: ReadOperation,
|
||||
req := &logical.Request{
|
||||
Operation: logical.ReadOperation,
|
||||
Path: "sys/mounts",
|
||||
}
|
||||
_, err := c.HandleRequest(req)
|
||||
|
|
|
@ -136,14 +136,14 @@ func (m *ExpirationManager) RevokePrefix(prefix string) error {
|
|||
|
||||
// Renew is used to renew a secret using the given vaultID
|
||||
// and a renew interval. The increment may be ignored.
|
||||
func (m *ExpirationManager) Renew(vaultID string, increment time.Duration) (*Lease, error) {
|
||||
func (m *ExpirationManager) Renew(vaultID string, increment time.Duration) (*logical.Lease, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Register is used to take a request and response with an associated
|
||||
// lease. The secret gets assigned a vaultId and the management of
|
||||
// of lease is assumed by the expiration manager.
|
||||
func (m *ExpirationManager) Register(req *Request, resp *Response) (string, error) {
|
||||
func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Response) (string, error) {
|
||||
// Ignore if there is no lease
|
||||
if resp == nil || resp.Lease == nil {
|
||||
return "", nil
|
||||
|
@ -195,7 +195,7 @@ type leaseEntry struct {
|
|||
VaultID string
|
||||
Path string
|
||||
Data map[string]interface{}
|
||||
Lease *Lease
|
||||
Lease *logical.Lease
|
||||
IssueTime time.Time
|
||||
RenewTime time.Time
|
||||
}
|
||||
|
|
|
@ -5,12 +5,28 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
||||
// mockExpiration returns a mock expiration manager
|
||||
func mockExpiration(t *testing.T) *ExpirationManager {
|
||||
inm := physical.NewInmem()
|
||||
b, err := NewAESGCMBarrier(inm)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Initialize and unseal
|
||||
key, _ := b.GenerateKey()
|
||||
b.Initialize(key)
|
||||
b.Unseal(key)
|
||||
|
||||
// Create the barrier view
|
||||
view := NewBarrierView(b, "expire/")
|
||||
|
||||
router := NewRouter()
|
||||
view := mockView(t, "expire/")
|
||||
return NewExpirationManager(router, view)
|
||||
}
|
||||
|
||||
|
@ -34,13 +50,13 @@ func TestExpiration_StartStop(t *testing.T) {
|
|||
|
||||
func TestExpiration_Register(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
req := &Request{
|
||||
Operation: ReadOperation,
|
||||
req := &logical.Request{
|
||||
Operation: logical.ReadOperation,
|
||||
Path: "prod/aws/foo",
|
||||
}
|
||||
resp := &Response{
|
||||
resp := &logical.Response{
|
||||
IsSecret: true,
|
||||
Lease: &Lease{
|
||||
Lease: &logical.Lease{
|
||||
Duration: time.Hour,
|
||||
MaxDuration: time.Hour,
|
||||
},
|
||||
|
@ -71,7 +87,7 @@ func TestLeaseEntry(t *testing.T) {
|
|||
Data: map[string]interface{}{
|
||||
"testing": true,
|
||||
},
|
||||
Lease: &Lease{
|
||||
Lease: &logical.Lease{
|
||||
Renewable: true,
|
||||
Duration: time.Minute,
|
||||
MaxDuration: time.Hour,
|
||||
|
|
152
vault/generic.go
152
vault/generic.go
|
@ -1,152 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// GenericBackend is used for the storing generic secrets. These are not
|
||||
// materialized in any way. The value that is written to this backend
|
||||
// is the same value that is always returned. Leasing can be configured on
|
||||
// a per-key basis.
|
||||
type GenericBackend struct{}
|
||||
|
||||
// newGenericBackend is a factory constructor for the generic backend
|
||||
func newGenericBackend(map[string]string) (LogicalBackend, error) {
|
||||
b := &GenericBackend{}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// HandleRequest is used to handle a request and generate a response.
|
||||
// The backends must check the operation type and handle appropriately.
|
||||
func (g *GenericBackend) HandleRequest(req *Request) (*Response, error) {
|
||||
switch req.Operation {
|
||||
case ReadOperation:
|
||||
return g.handleRead(req)
|
||||
case WriteOperation:
|
||||
return g.handleWrite(req)
|
||||
case ListOperation:
|
||||
return g.handleList(req)
|
||||
case DeleteOperation:
|
||||
return g.handleDelete(req)
|
||||
case HelpOperation:
|
||||
return g.handleHelp(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
}
|
||||
|
||||
// RootPaths is a list of paths that require root level privileges,
|
||||
// which do not exist for the geneirc backend.
|
||||
func (g *GenericBackend) RootPaths() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GenericBackend) handleRead(req *Request) (*Response, error) {
|
||||
// Read the path
|
||||
out, err := req.Storage.Get(req.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read failed: %v", err)
|
||||
}
|
||||
|
||||
// Fast-path the no data case
|
||||
if out == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Decode the data
|
||||
var raw map[string]interface{}
|
||||
if err := json.Unmarshal(out.Value, &raw); err != nil {
|
||||
return nil, fmt.Errorf("json decoding failed: %v", err)
|
||||
}
|
||||
|
||||
// Check if there is a lease key
|
||||
leaseVal, ok := raw["lease"].(string)
|
||||
var lease *Lease
|
||||
if ok {
|
||||
leaseDuration, err := time.ParseDuration(leaseVal)
|
||||
if err == nil {
|
||||
lease = &Lease{
|
||||
Renewable: false,
|
||||
Revokable: false,
|
||||
Duration: leaseDuration,
|
||||
MaxDuration: leaseDuration,
|
||||
MaxIncrement: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the response
|
||||
resp := &Response{
|
||||
IsSecret: true,
|
||||
Lease: lease,
|
||||
Data: raw,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GenericBackend) handleWrite(req *Request) (*Response, error) {
|
||||
// Check that some fields are given
|
||||
if len(req.Data) == 0 {
|
||||
return nil, fmt.Errorf("missing data fields")
|
||||
}
|
||||
|
||||
// JSON encode the data
|
||||
buf, err := json.Marshal(req.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json encoding failed: %v", err)
|
||||
}
|
||||
|
||||
// Write out a new key
|
||||
entry := &logical.StorageEntry{
|
||||
Key: req.Path,
|
||||
Value: buf,
|
||||
}
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, fmt.Errorf("failed to write: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *GenericBackend) handleDelete(req *Request) (*Response, error) {
|
||||
// Delete the key at the request path
|
||||
if err := req.Storage.Delete(req.Path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *GenericBackend) handleList(req *Request) (*Response, error) {
|
||||
// List the keys at the prefix given by the request
|
||||
keys, err := req.Storage.List(req.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate the response
|
||||
resp := &Response{
|
||||
IsSecret: false,
|
||||
Lease: nil,
|
||||
Data: map[string]interface{}{
|
||||
"keys": keys,
|
||||
},
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (g *GenericBackend) handleHelp(req *Request) (*Response, error) {
|
||||
resp := &Response{
|
||||
IsSecret: false,
|
||||
Lease: nil,
|
||||
Data: map[string]interface{}{
|
||||
"help": genericHelpText,
|
||||
},
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// genericHelpText is the help information we return
|
||||
const genericHelpText = "Generic backend for storing and retreiving raw keys with user-defined fields"
|
|
@ -1,228 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
||||
// mockView returns a view attached to a barrier / backend
|
||||
func mockView(t *testing.T, prefix string) *BarrierView {
|
||||
inm := physical.NewInmem()
|
||||
b, err := NewAESGCMBarrier(inm)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Initialize and unseal
|
||||
key, _ := b.GenerateKey()
|
||||
b.Initialize(key)
|
||||
b.Unseal(key)
|
||||
|
||||
// Create the barrier view
|
||||
view := NewBarrierView(b, prefix)
|
||||
return view
|
||||
}
|
||||
|
||||
// mockRequest returns a request with a real view attached
|
||||
func mockRequest(t *testing.T, op Operation, path string) *Request {
|
||||
view := mockView(t, "logical/")
|
||||
|
||||
// Create the request
|
||||
req := &Request{
|
||||
Operation: op,
|
||||
Path: path,
|
||||
Data: make(map[string]interface{}),
|
||||
Storage: view,
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func TestGenericBackend_RootPaths(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
root := b.RootPaths()
|
||||
if len(root) != 0 {
|
||||
t.Fatalf("unexpected: %v", root)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericBackend_Write(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := mockRequest(t, WriteOperation, "foo")
|
||||
req.Data["raw"] = "test"
|
||||
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
|
||||
out, err := req.Storage.Get("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
t.Fatalf("failed to write to view")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericBackend_Read(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := mockRequest(t, WriteOperation, "foo")
|
||||
req.Data["raw"] = "test"
|
||||
req.Data["lease"] = "1h"
|
||||
|
||||
if _, err := b.HandleRequest(req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req2 := mockRequest(t, ReadOperation, "foo")
|
||||
req2.Storage = req.Storage
|
||||
|
||||
resp, err := b.HandleRequest(req2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !resp.IsSecret {
|
||||
t.Fatalf("should be secret: %#v", resp)
|
||||
}
|
||||
|
||||
if resp.Lease == nil {
|
||||
t.Fatalf("should have lease: %#v", resp)
|
||||
}
|
||||
if resp.Lease.Renewable {
|
||||
t.Fatalf("bad lease: %#v", resp.Lease)
|
||||
}
|
||||
if resp.Lease.Revokable {
|
||||
t.Fatalf("bad lease: %#v", resp.Lease)
|
||||
}
|
||||
if resp.Lease.Duration != time.Hour {
|
||||
t.Fatalf("bad lease: %#v", resp.Lease)
|
||||
}
|
||||
if resp.Lease.MaxDuration != time.Hour {
|
||||
t.Fatalf("bad lease: %#v", resp.Lease)
|
||||
}
|
||||
if resp.Lease.MaxIncrement != 0 {
|
||||
t.Fatalf("bad lease: %#v", resp.Lease)
|
||||
}
|
||||
|
||||
if resp.Data["raw"] != "test" {
|
||||
t.Fatalf("bad data: %#v", resp.Data)
|
||||
}
|
||||
if resp.Data["lease"] != "1h" {
|
||||
t.Fatalf("bad data: %#v", resp.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericBackend_Delete(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := mockRequest(t, WriteOperation, "foo")
|
||||
req.Data["raw"] = "test"
|
||||
req.Data["lease"] = "1h"
|
||||
|
||||
if _, err := b.HandleRequest(req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req2 := mockRequest(t, DeleteOperation, "foo")
|
||||
req2.Storage = req.Storage
|
||||
|
||||
resp, err := b.HandleRequest(req2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
|
||||
req3 := mockRequest(t, ReadOperation, "foo")
|
||||
req3.Storage = req.Storage
|
||||
|
||||
resp, err = b.HandleRequest(req3)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericBackend_List(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := mockRequest(t, WriteOperation, "foo/bar")
|
||||
req.Data["raw"] = "test"
|
||||
req.Data["lease"] = "1h"
|
||||
|
||||
if _, err := b.HandleRequest(req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req2 := mockRequest(t, ListOperation, "")
|
||||
req2.Storage = req.Storage
|
||||
|
||||
resp, err := b.HandleRequest(req2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if resp.IsSecret {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
if resp.Lease != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
if resp.Data["keys"] == nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
keys := resp.Data["keys"].([]string)
|
||||
if len(keys) != 1 || keys[0] != "foo/" {
|
||||
t.Fatalf("keys: %v", keys)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericBackend_Help(t *testing.T) {
|
||||
b, err := newGenericBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := mockRequest(t, HelpOperation, "foo")
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if resp.IsSecret {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
if resp.Lease != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
if resp.Data["help"] != genericHelpText {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnsupportedOperation is returned if the operation is not supported
|
||||
// by the logical backend.
|
||||
ErrUnsupportedOperation = errors.New("unsupported operation")
|
||||
|
||||
// ErrUnsupportedPath is returned if the path is not supported
|
||||
// by the logical backend.
|
||||
ErrUnsupportedPath = errors.New("unsupported path")
|
||||
|
||||
// ErrInvalidRequest is returned if the request is invalid
|
||||
ErrInvalidRequest = errors.New("invalid request")
|
||||
)
|
||||
|
||||
// LogicalBackend interface must be implemented to be "mountable" at
|
||||
// a given path. Requests flow through a router which has various mount
|
||||
// points that flow to a logical backend. The logic of each backend is flexible,
|
||||
// and this is what allows materialized keys to function. There can be specialized
|
||||
// logical backends for various upstreams (Consul, PostgreSQL, MySQL, etc) that can
|
||||
// interact with remote APIs to generate keys dynamically. This interface also
|
||||
// allows for a "procfs" like interaction, as internal state can be exposed by
|
||||
// acting like a logical backend and being mounted.
|
||||
type LogicalBackend interface {
|
||||
// HandleRequest is used to handle a request and generate a response.
|
||||
// The backends must check the operation type and handle appropriately.
|
||||
HandleRequest(*Request) (*Response, error)
|
||||
|
||||
// RootPaths is a list of paths that require root level privileges.
|
||||
// These paths will be enforced by the router so that backends do
|
||||
// not need to handle the authorization. Paths are enforced exactly
|
||||
// or using a prefix match if they end in '*'
|
||||
RootPaths() []string
|
||||
}
|
||||
|
||||
// Operation is an enum that is used to specify the type
|
||||
// of request being made
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
ReadOperation Operation = "read"
|
||||
WriteOperation = "write"
|
||||
DeleteOperation = "delete"
|
||||
ListOperation = "list"
|
||||
RevokeOperation = "revoke"
|
||||
HelpOperation = "help"
|
||||
)
|
||||
|
||||
// Request is a struct that stores the parameters and context
|
||||
// of a request being made to Vault. It is used to abstract
|
||||
// the details of the higher level request protocol from the handlers.
|
||||
type Request struct {
|
||||
// Operation is the requested operation type
|
||||
Operation Operation
|
||||
|
||||
// Path is the part of the request path not consumed by the
|
||||
// routing. As an example, if the original request path is "prod/aws/foo"
|
||||
// and the AWS logical backend is mounted at "prod/aws/", then the
|
||||
// final path is "foo" since the mount prefix is trimmed.
|
||||
Path string
|
||||
|
||||
// Request data is an opaque map that must have string keys.
|
||||
Data map[string]interface{}
|
||||
|
||||
// View is the storage view of this logical backend. It can be used
|
||||
// to durably store and retrieve state from the backend.
|
||||
Storage logical.Storage
|
||||
}
|
||||
|
||||
// Get returns a data field and guards for nil Data
|
||||
func (r *Request) Get(key string) interface{} {
|
||||
if r.Data == nil {
|
||||
return nil
|
||||
}
|
||||
return r.Data[key]
|
||||
}
|
||||
|
||||
// GetString returns a data field as a string
|
||||
func (r *Request) GetString(key string) string {
|
||||
raw := r.Get(key)
|
||||
s, _ := raw.(string)
|
||||
return s
|
||||
}
|
||||
|
||||
// Response is a struct that stores the response of a request.
|
||||
// It is used to abstract the details of the higher level request protocol.
|
||||
type Response struct {
|
||||
// IsSecret is used to indicate this is secret material instead of policy or configuration.
|
||||
// Non-secrets never have a VaultID or renewable properties.
|
||||
IsSecret bool
|
||||
|
||||
// The lease settings if applicable.
|
||||
Lease *Lease
|
||||
|
||||
// Response data is an opaque map that must have string keys.
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
// Lease is used to provide more information about the lease
|
||||
type Lease struct {
|
||||
VaultID string // VaultID is the unique identifier used for renewal and revocation
|
||||
Renewable bool // Is the VaultID renewable
|
||||
Revokable bool // Is the secret revokable. Must support 'Revoke' operation.
|
||||
Duration time.Duration // Current lease duration
|
||||
MaxDuration time.Duration // Maximum lease duration
|
||||
MaxIncrement time.Duration // Maximum increment to lease duration
|
||||
}
|
||||
|
||||
// Validate is used to sanity check a lease
|
||||
func (l *Lease) Validate() error {
|
||||
if l.Duration <= 0 {
|
||||
return fmt.Errorf("lease duration must be greater than zero")
|
||||
}
|
||||
if l.MaxDuration <= 0 {
|
||||
return fmt.Errorf("maximum lease duration must be greater than zero")
|
||||
}
|
||||
if l.Duration > l.MaxDuration {
|
||||
return fmt.Errorf("lease duration cannot be greater than maximum lease duration")
|
||||
}
|
||||
if l.MaxIncrement < 0 {
|
||||
return fmt.Errorf("maximum lease increment cannot be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Factory is the factory function to create a logical backend.
|
||||
type Factory func(map[string]string) (LogicalBackend, error)
|
||||
|
||||
// BuiltinBackends contains all of the available backends
|
||||
var BuiltinBackends = map[string]Factory{
|
||||
"generic": newGenericBackend,
|
||||
}
|
||||
|
||||
// NewBackend returns a new logical Backend with the given type and configuration.
|
||||
// The backend is looked up in the BuiltinBackends variable.
|
||||
func NewBackend(t string, conf map[string]string) (LogicalBackend, error) {
|
||||
f, ok := BuiltinBackends[t]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown logical backend type: %s", t)
|
||||
}
|
||||
return f(conf)
|
||||
}
|
||||
|
||||
// HelpResponse is used to format a help response
|
||||
func HelpResponse(text string, seeAlso []string) *Response {
|
||||
return &Response{
|
||||
IsSecret: false,
|
||||
Data: map[string]interface{}{
|
||||
"help": text,
|
||||
"see_also": seeAlso,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorResponse is used to format an error response
|
||||
func ErrorResponse(text string) *Response {
|
||||
return &Response{
|
||||
IsSecret: false,
|
||||
Data: map[string]interface{}{
|
||||
"error": text,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLease_Validate(t *testing.T) {
|
||||
l := &Lease{}
|
||||
if err := l.Validate(); err.Error() != "lease duration must be greater than zero" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
l.Duration = time.Minute
|
||||
if err := l.Validate(); err.Error() != "maximum lease duration must be greater than zero" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
l.MaxDuration = time.Second
|
||||
if err := l.Validate(); err.Error() != "lease duration cannot be greater than maximum lease duration" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
l.MaxDuration = time.Minute
|
||||
l.MaxIncrement = -1 * time.Second
|
||||
if err := l.Validate(); err.Error() != "maximum lease increment cannot be negative" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
l.MaxIncrement = time.Second
|
||||
if err := l.Validate(); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
|
@ -8,6 +8,11 @@ import (
|
|||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// logical.Factory
|
||||
func PassthroughBackendFactory(map[string]string) (logical.Backend, error) {
|
||||
return new(PassthroughBackend), nil
|
||||
}
|
||||
|
||||
// PassthroughBackend is used storing secrets directly into the physical
|
||||
// backend. The secrest are encrypted in the durable storage and custom lease
|
||||
// information can be specified, but otherwise this backend doesn't do anything
|
||||
|
@ -27,7 +32,7 @@ func (b *PassthroughBackend) HandleRequest(req *logical.Request) (*logical.Respo
|
|||
case logical.ListOperation:
|
||||
return b.handleList(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
return nil, logical.ErrUnsupportedOperation
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func (b *SystemBackend2) HandleRequest(req *logical.Request) (*logical.Response,
|
|||
case req.Path == "remount":
|
||||
return b.handleRemount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedPath
|
||||
return nil, logical.ErrUnsupportedPath
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ func (b *SystemBackend2) handleMountTable(req *logical.Request) (*logical.Respon
|
|||
switch req.Operation {
|
||||
case logical.ReadOperation:
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
return nil, logical.ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
b.Core.mountsLock.RLock()
|
||||
|
@ -68,7 +68,7 @@ func (b *SystemBackend2) handleMountOperation(req *logical.Request) (*logical.Re
|
|||
case logical.DeleteOperation:
|
||||
return b.handleUnmount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
return nil, logical.ErrUnsupportedOperation
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,13 +76,13 @@ func (b *SystemBackend2) handleMountOperation(req *logical.Request) (*logical.Re
|
|||
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
|
||||
return logical.ErrorResponse("path cannot be blank"), logical.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
|
||||
return logical.ErrorResponse("backend type must be specified as a string"), logical.ErrInvalidRequest
|
||||
}
|
||||
description := req.GetString("description")
|
||||
|
||||
|
@ -95,7 +95,7 @@ func (b *SystemBackend2) handleMount(req *logical.Request) (*logical.Response, e
|
|||
|
||||
// Attempt mount
|
||||
if err := b.Core.mount(me); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -104,12 +104,12 @@ func (b *SystemBackend2) handleMount(req *logical.Request) (*logical.Response, e
|
|||
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
|
||||
return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt unmount
|
||||
if err := b.Core.unmount(suffix); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
@ -119,9 +119,9 @@ func (b *SystemBackend2) handleUnmount(req *logical.Request) (*logical.Response,
|
|||
func (b *SystemBackend2) handleRemount(req *logical.Request) (*logical.Response, error) {
|
||||
// Only accept write operations
|
||||
switch req.Operation {
|
||||
case WriteOperation:
|
||||
case logical.WriteOperation:
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
return nil, logical.ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// Get the paths
|
||||
|
@ -130,12 +130,12 @@ func (b *SystemBackend2) handleRemount(req *logical.Request) (*logical.Response,
|
|||
if fromPath == "" || toPath == "" {
|
||||
return logical.ErrorResponse(
|
||||
"both 'from' and 'to' path must be specified as a string"),
|
||||
ErrInvalidRequest
|
||||
logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt remount
|
||||
if err := b.Core.remount(fromPath, toPath); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
|
|
@ -68,7 +68,7 @@ func TestSystemBackend_mount_invalid(t *testing.T) {
|
|||
req := logical.TestRequest(t, logical.WriteOperation, "mount/prod/secret/")
|
||||
req.Data["type"] = "nope"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "unknown logical backend type: nope" {
|
||||
|
@ -94,7 +94,7 @@ func TestSystemBackend_unmount_invalid(t *testing.T) {
|
|||
|
||||
req := logical.TestRequest(t, logical.DeleteOperation, "mount/foo/")
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount" {
|
||||
|
@ -124,7 +124,7 @@ func TestSystemBackend_remount_invalid(t *testing.T) {
|
|||
req.Data["from"] = "unknown"
|
||||
req.Data["to"] = "foo"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount at 'unknown/'" {
|
||||
|
@ -139,7 +139,7 @@ func TestSystemBackend_remount_system(t *testing.T) {
|
|||
req.Data["from"] = "sys"
|
||||
req.Data["to"] = "foo"
|
||||
resp, err := b.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
if err != logical.ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "cannot remount 'sys/'" {
|
||||
|
|
|
@ -5,8 +5,29 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// TEMPORARY!
|
||||
|
||||
// BuiltinBackends contains all of the available backends
|
||||
var BuiltinBackends = map[string]logical.Factory{
|
||||
"generic": PassthroughBackendFactory,
|
||||
}
|
||||
|
||||
// NewBackend returns a new logical Backend with the given type and configuration.
|
||||
// The backend is looked up in the BuiltinBackends variable.
|
||||
func NewBackend(t string, conf map[string]string) (logical.Backend, error) {
|
||||
f, ok := BuiltinBackends[t]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown logical backend type: %s", t)
|
||||
}
|
||||
return f(conf)
|
||||
}
|
||||
|
||||
// TEMPORARY!
|
||||
|
||||
const (
|
||||
// coreMountConfigPath is used to store the mount configuration.
|
||||
// Mounts are protected within the Vault itself, which means they
|
||||
|
@ -262,13 +283,13 @@ func (c *Core) persistMounts(table *MountTable) error {
|
|||
// setupMounts is invoked after we've loaded the mount table to
|
||||
// initialize the logical backends and setup the router
|
||||
func (c *Core) setupMounts() error {
|
||||
var backend LogicalBackend
|
||||
var backend logical.Backend
|
||||
var view *BarrierView
|
||||
var err error
|
||||
for _, entry := range c.mounts.Entries {
|
||||
// Initialize the backend, special casing for system
|
||||
if entry.Type == "system" {
|
||||
backend = &SystemBackend{core: c}
|
||||
backend = &SystemBackend2{Core: c}
|
||||
view = NewBarrierView(c.barrier, systemBarrierPrefix+entry.UUID+"/")
|
||||
c.systemView = view
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/armon/go-radix"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// Router is used to do prefix based routing of a request to a logical backend
|
||||
|
@ -25,13 +26,13 @@ func NewRouter() *Router {
|
|||
// mountEntry is used to represent a mount point
|
||||
type mountEntry struct {
|
||||
mtype string
|
||||
backend LogicalBackend
|
||||
backend logical.Backend
|
||||
view *BarrierView
|
||||
rootPaths *radix.Tree
|
||||
}
|
||||
|
||||
// Mount is used to expose a logical backend at a given prefix
|
||||
func (r *Router) Mount(backend LogicalBackend, mtype, prefix string, view *BarrierView) error {
|
||||
func (r *Router) Mount(backend logical.Backend, mtype, prefix string, view *BarrierView) error {
|
||||
r.l.Lock()
|
||||
defer r.l.Unlock()
|
||||
|
||||
|
@ -103,7 +104,7 @@ func (r *Router) MatchingMount(path string) string {
|
|||
}
|
||||
|
||||
// Route is used to route a given request
|
||||
func (r *Router) Route(req *Request) (*Response, error) {
|
||||
func (r *Router) Route(req *logical.Request) (*logical.Response, error) {
|
||||
// Find the mount point
|
||||
r.l.RLock()
|
||||
mount, raw, ok := r.root.LongestPrefix(req.Path)
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
type NoopBackend struct {
|
||||
|
@ -11,11 +13,12 @@ type NoopBackend struct {
|
|||
Paths []string
|
||||
}
|
||||
|
||||
func (n *NoopBackend) HandleRequest(req *Request) (*Response, error) {
|
||||
func (n *NoopBackend) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||
n.Paths = append(n.Paths, req.Path)
|
||||
if req.Storage == nil {
|
||||
return nil, fmt.Errorf("missing view")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -47,7 +50,7 @@ func TestRouter_Mount(t *testing.T) {
|
|||
t.Fatalf("bad: %s", path)
|
||||
}
|
||||
|
||||
req := &Request{
|
||||
req := &logical.Request{
|
||||
Path: "prod/aws/foo",
|
||||
}
|
||||
resp, err := r.Route(req)
|
||||
|
@ -80,7 +83,7 @@ func TestRouter_Unmount(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := &Request{
|
||||
req := &logical.Request{
|
||||
Path: "prod/aws/foo",
|
||||
}
|
||||
_, err = r.Route(req)
|
||||
|
@ -110,7 +113,7 @@ func TestRouter_Remount(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := &Request{
|
||||
req := &logical.Request{
|
||||
Path: "prod/aws/foo",
|
||||
}
|
||||
_, err = r.Route(req)
|
||||
|
@ -118,7 +121,7 @@ func TestRouter_Remount(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req = &Request{
|
||||
req = &logical.Request{
|
||||
Path: "stage/aws/foo",
|
||||
}
|
||||
_, err = r.Route(req)
|
||||
|
|
139
vault/system.go
139
vault/system.go
|
@ -1,139 +0,0 @@
|
|||
package vault
|
||||
|
||||
import "strings"
|
||||
|
||||
// SystemBackend implements the LogicalBackend interface but is used
|
||||
// to interact with the core of the system. It acts like a "procfs"
|
||||
// to provide a uniform interface to vault.
|
||||
type SystemBackend struct {
|
||||
core *Core
|
||||
}
|
||||
|
||||
func (s *SystemBackend) HandleRequest(req *Request) (*Response, error) {
|
||||
// Switch on the path to route to the appropriate handler
|
||||
switch {
|
||||
case req.Path == "mounts":
|
||||
return s.handleMountTable(req)
|
||||
case strings.HasPrefix(req.Path, "mount/"):
|
||||
return s.handleMountOperation(req)
|
||||
case req.Path == "remount":
|
||||
return s.handleRemount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedPath
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SystemBackend) RootPaths() []string {
|
||||
return []string{
|
||||
"mount/*",
|
||||
"remount",
|
||||
}
|
||||
}
|
||||
|
||||
// handleMountTable handles the "mounts" endpoint to provide the mount table
|
||||
func (s *SystemBackend) handleMountTable(req *Request) (*Response, error) {
|
||||
switch req.Operation {
|
||||
case ReadOperation:
|
||||
case HelpOperation:
|
||||
return HelpResponse("logical backend mount table", []string{"sys/mount/"}), nil
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
s.core.mountsLock.RLock()
|
||||
defer s.core.mountsLock.RUnlock()
|
||||
|
||||
resp := &Response{
|
||||
IsSecret: false,
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
for _, entry := range s.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 (s *SystemBackend) handleMountOperation(req *Request) (*Response, error) {
|
||||
switch req.Operation {
|
||||
case WriteOperation:
|
||||
return s.handleMount(req)
|
||||
case DeleteOperation:
|
||||
return s.handleUnmount(req)
|
||||
case HelpOperation:
|
||||
return HelpResponse("used to mount or unmount a path", []string{"sys/mounts"}), nil
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
}
|
||||
|
||||
// handleMount is used to mount a new path
|
||||
func (s *SystemBackend) handleMount(req *Request) (*Response, error) {
|
||||
suffix := strings.TrimPrefix(req.Path, "mount/")
|
||||
if len(suffix) == 0 {
|
||||
return ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Get the type and description (optionally)
|
||||
logicalType := req.GetString("type")
|
||||
if logicalType == "" {
|
||||
return 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 := s.core.mount(me); err != nil {
|
||||
return ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleUnmount is used to unmount a path
|
||||
func (s *SystemBackend) handleUnmount(req *Request) (*Response, error) {
|
||||
suffix := strings.TrimPrefix(req.Path, "mount/")
|
||||
if len(suffix) == 0 {
|
||||
return ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt unmount
|
||||
if err := s.core.unmount(suffix); err != nil {
|
||||
return ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleRemount is used to remount a path
|
||||
func (s *SystemBackend) handleRemount(req *Request) (*Response, error) {
|
||||
// Only accept write operations
|
||||
switch req.Operation {
|
||||
case WriteOperation:
|
||||
case HelpOperation:
|
||||
return HelpResponse("remount a backend path", []string{"sys/mount/", "sys/mounts"}), nil
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// Get the paths
|
||||
fromPath := req.GetString("from")
|
||||
toPath := req.GetString("to")
|
||||
if fromPath == "" || toPath == "" {
|
||||
return ErrorResponse("both 'from' and 'to' path must be specified as a string"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt remount
|
||||
if err := s.core.remount(fromPath, toPath); err != nil {
|
||||
return ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
|
@ -1,238 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testSystem(t *testing.T) *SystemBackend {
|
||||
c, _ := TestCoreUnsealed(t)
|
||||
return &SystemBackend{c}
|
||||
}
|
||||
|
||||
func TestSystem_verifyRoot(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
r := NewRouter()
|
||||
r.Mount(s, "system", "sys/", nil)
|
||||
|
||||
root := []string{
|
||||
"sys/mount/prod/",
|
||||
"sys/remount",
|
||||
}
|
||||
nonRoot := []string{
|
||||
"sys/mounts",
|
||||
}
|
||||
|
||||
for _, key := range root {
|
||||
if !r.RootPath(key) {
|
||||
t.Fatalf("expected '%s' to be root path", key)
|
||||
}
|
||||
}
|
||||
for _, key := range nonRoot {
|
||||
if r.RootPath(key) {
|
||||
t.Fatalf("expected '%s' to be non-root path", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_mounts(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: ReadOperation,
|
||||
Path: "mounts",
|
||||
}
|
||||
resp, err := s.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)
|
||||
}
|
||||
|
||||
req = &Request{
|
||||
Operation: HelpOperation,
|
||||
Path: "mounts",
|
||||
}
|
||||
resp, err = s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["help"] != "logical backend mount table" {
|
||||
t.Fatalf("got: %#v", resp.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_mount_help(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: HelpOperation,
|
||||
Path: "mount/prod/secret/",
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["help"] != "used to mount or unmount a path" {
|
||||
t.Fatalf("got: %#v", resp.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_mount(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "mount/prod/secret/",
|
||||
Data: map[string]interface{}{
|
||||
"type": "generic",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_mount_invalid(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "mount/prod/secret/",
|
||||
Data: map[string]interface{}{
|
||||
"type": "what",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "unknown logical backend type: what" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_unmount(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: DeleteOperation,
|
||||
Path: "mount/secret/",
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_unmount_invalid(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: DeleteOperation,
|
||||
Path: "mount/foo/",
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "secret",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount_invalid(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "unknown",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.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 TestSystem_remount_system(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "sys",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "cannot remount 'sys/'" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount_help(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: HelpOperation,
|
||||
Path: "remount",
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["help"] != "remount a backend path" {
|
||||
t.Fatalf("got: %#v", resp.Data)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue