vault: Adding lease registration

This commit is contained in:
Armon Dadgar
2015-03-13 10:55:54 -07:00
parent f34ade56ad
commit 94bdee625f
3 changed files with 156 additions and 5 deletions

View File

@@ -1,6 +1,11 @@
package vault
import "time"
import (
"encoding/json"
"fmt"
"path"
"time"
)
const (
// expirationSubPath is the sub-path used for the expiration manager
@@ -61,5 +66,68 @@ func (m *ExpirationManager) Renew(vaultID string, increment time.Duration) (*Lea
// 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) {
return "", nil
// Ignore if there is no lease
if resp == nil || resp.Lease == nil {
return "", nil
}
// Validate the lease
if err := resp.Lease.Validate(); err != nil {
return "", err
}
// Cannot register a non-secret (e.g. a policy or configuration key)
if !resp.IsSecret {
return "", fmt.Errorf("cannot attach lease to non-secret")
}
// Create a lease entry
le := leaseEntry{
VaultID: path.Join(req.Path, generateUUID()),
Path: req.Path,
Data: resp.Data,
Lease: resp.Lease,
IssueTime: time.Now().UTC(),
}
// Encode the entry
buf, err := le.encode()
if err != nil {
return "", fmt.Errorf("failed to encode lease entry: %v", err)
}
// Write out to the view
ent := Entry{
Key: le.VaultID,
Value: buf,
}
if err := m.view.Put(&ent); err != nil {
return "", fmt.Errorf("failed to persist lease entry: %v", err)
}
// TODO: Automatic revoke timer...
// Done
return le.VaultID, nil
}
// leaseEntry is used to structure the values the expiration
// manager stores. This is used to handle renew and revocation.
type leaseEntry struct {
VaultID string
Path string
Data map[string]interface{}
Lease *Lease
IssueTime time.Time
}
// encode is used to JSON encode the lease entry
func (l *leaseEntry) encode() ([]byte, error) {
return json.Marshal(l)
}
// decodeLeaseEntry is used to reverse encode and return a new entry
func decodeLeaseEntry(buf []byte) (*leaseEntry, error) {
out := new(leaseEntry)
return out, json.Unmarshal(buf, out)
}

77
vault/expiration_test.go Normal file
View File

@@ -0,0 +1,77 @@
package vault
import (
"reflect"
"strings"
"testing"
"time"
)
// mockExpiration returns a mock expiration manager
func mockExpiration(t *testing.T) *ExpirationManager {
router := NewRouter()
view := mockView(t, "expire/")
return NewExpirationManager(router, view)
}
func TestExpiration_Register(t *testing.T) {
exp := mockExpiration(t)
req := &Request{
Operation: ReadOperation,
Path: "prod/aws/foo",
}
resp := &Response{
IsSecret: true,
Lease: &Lease{
Duration: time.Hour,
MaxDuration: time.Hour,
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
if !strings.HasPrefix(id, req.Path) {
t.Fatalf("bad: %s", id)
}
if len(id) <= len(req.Path) {
t.Fatalf("bad: %s", id)
}
}
func TestLeaseEntry(t *testing.T) {
le := &leaseEntry{
VaultID: "foo/bar/1234",
Path: "foo/bar",
Data: map[string]interface{}{
"testing": true,
},
Lease: &Lease{
Renewable: true,
Duration: time.Minute,
MaxDuration: time.Hour,
},
IssueTime: time.Now(),
}
enc, err := le.encode()
if err != nil {
t.Fatalf("err: %v", err)
}
out, err := decodeLeaseEntry(enc)
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(out.Data, le.Data) {
t.Fatalf("got: %#v, expect %#v", out, le)
}
}

View File

@@ -7,8 +7,8 @@ import (
"github.com/hashicorp/vault/physical"
)
// mockRequest returns a request with a real view attached
func mockRequest(t *testing.T, op Operation, path string) *Request {
// 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 {
@@ -21,7 +21,13 @@ func mockRequest(t *testing.T, op Operation, path string) *Request {
b.Unseal(key)
// Create the barrier view
view := NewBarrierView(b, "logical/")
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{