Skip to content

Commit

Permalink
vault: Passthrough backend uses logical.Backend
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Mar 15, 2015
1 parent 5ce1b65 commit 68918fe
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 0 deletions.
9 changes: 9 additions & 0 deletions logical/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,12 @@ func ErrorResponse(text string) *Response {
},
}
}

// ListResponse is used to format a response to a list operation.
func ListResponse(keys []string) *Response {
return &Response{
Data: map[string]interface{}{
"keys": keys,
},
}
}
47 changes: 47 additions & 0 deletions logical/storage_inmem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package logical

import (
"strings"
"sync"
)

// InmemStorage implements Storage and stores all data in memory.
type InmemStorage struct {
Data map[string]*StorageEntry

once sync.Once
}

func (s *InmemStorage) List(prefix string) ([]string, error) {
s.once.Do(s.init)

var result []string
for k, _ := range s.Data {
if strings.HasPrefix(k, prefix) {
result = append(result, k)
}
}

return result, nil
}

func (s *InmemStorage) Get(key string) (*StorageEntry, error) {
s.once.Do(s.init)
return s.Data[key], nil
}

func (s *InmemStorage) Put(entry *StorageEntry) error {
s.once.Do(s.init)
s.Data[entry.Key] = entry
return nil
}

func (s *InmemStorage) Delete(k string) error {
s.once.Do(s.init)
delete(s.Data, k)
return nil
}

func (s *InmemStorage) init() {
s.Data = make(map[string]*StorageEntry)
}
9 changes: 9 additions & 0 deletions logical/storage_inmem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package logical

import (
"testing"
)

func TestInmemStorage(t *testing.T) {
TestStorage(t, new(InmemStorage))
}
61 changes: 61 additions & 0 deletions logical/testing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package logical

import (
"reflect"
"testing"
)

// TestRequest is a helper to create a purely in-memory Request struct.
func TestRequest(t *testing.T, op Operation, path string) *Request {
return &Request{
Operation: op,
Path: path,
Data: make(map[string]interface{}),
Storage: new(InmemStorage),
}
}

// TestStorage is a helper that can be used from unit tests to verify
// the behavior of a Storage impl.
func TestStorage(t *testing.T, s Storage) {
keys, err := s.List("")
if err != nil {
t.Fatalf("list error: %s", err)
}
if len(keys) > 0 {
t.Fatalf("should have no keys to start: %#v", keys)
}

entry := &StorageEntry{Key: "foo", Value: []byte("bar")}
if err := s.Put(entry); err != nil {
t.Fatalf("put error: %s", err)
}

actual, err := s.Get("foo")
if err != nil {
t.Fatalf("get error: %s", err)
}
if !reflect.DeepEqual(actual, entry) {
t.Fatalf("wrong value. Expected: %#v\nGot: %#v", entry, actual)
}

keys, err = s.List("")
if err != nil {
t.Fatalf("list error: %s", err)
}
if !reflect.DeepEqual(keys, []string{"foo"}) {
t.Fatalf("bad keys: %#v", keys)
}

if err := s.Delete("foo"); err != nil {
t.Fatalf("put error: %s", err)
}

keys, err = s.List("")
if err != nil {
t.Fatalf("list error: %s", err)
}
if len(keys) > 0 {
t.Fatalf("should have no keys to start: %#v", keys)
}
}
6 changes: 6 additions & 0 deletions vault/barrier_view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ func TestBarrierView_impl(t *testing.T) {
var _ logical.Storage = new(BarrierView)
}

func TestBarrierView_spec(t *testing.T) {
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "foo/")
logical.TestStorage(t, view)
}

func TestBarrierView(t *testing.T) {
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "foo/")
Expand Down
123 changes: 123 additions & 0 deletions vault/logical_passthrough.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package vault

import (
"encoding/json"
"fmt"
"time"

"github.com/hashicorp/vault/logical"
)

// 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
// fancy.
type PassthroughBackend struct{}

func (b *PassthroughBackend) HandleRequest(req *logical.Request) (*logical.Response, error) {
// TODO(mitchellh): help, let's just do it when we migrate to helper/backend

switch req.Operation {
case logical.ReadOperation:
return b.handleRead(req)
case logical.WriteOperation:
return b.handleWrite(req)
case logical.DeleteOperation:
return b.handleDelete(req)
case logical.ListOperation:
return b.handleList(req)
default:
return nil, ErrUnsupportedOperation
}
}

func (b *PassthroughBackend) RootPaths() []string {
return nil
}

func (b *PassthroughBackend) handleRead(req *logical.Request) (*logical.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 *logical.Lease
if ok {
leaseDuration, err := time.ParseDuration(leaseVal)
if err == nil {
lease = &logical.Lease{
Renewable: false,
Revokable: false,
Duration: leaseDuration,
MaxDuration: leaseDuration,
MaxIncrement: 0,
}
}
}

// Generate the response
resp := &logical.Response{
IsSecret: true,
Lease: lease,
Data: raw,
}
return resp, nil
}

func (b *PassthroughBackend) handleWrite(req *logical.Request) (*logical.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 (b *PassthroughBackend) handleDelete(req *logical.Request) (*logical.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 (b *PassthroughBackend) handleList(req *logical.Request) (*logical.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
return logical.ListResponse(keys), nil
}
Loading

0 comments on commit 68918fe

Please sign in to comment.