mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
vault: Adding lease registration
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
package vault
|
package vault
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// expirationSubPath is the sub-path used for the expiration manager
|
// 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
|
// lease. The secret gets assigned a vaultId and the management of
|
||||||
// of lease is assumed by the expiration manager.
|
// of lease is assumed by the expiration manager.
|
||||||
func (m *ExpirationManager) Register(req *Request, resp *Response) (string, error) {
|
func (m *ExpirationManager) Register(req *Request, resp *Response) (string, error) {
|
||||||
|
// Ignore if there is no lease
|
||||||
|
if resp == nil || resp.Lease == nil {
|
||||||
return "", 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
77
vault/expiration_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"github.com/hashicorp/vault/physical"
|
"github.com/hashicorp/vault/physical"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mockRequest returns a request with a real view attached
|
// mockView returns a view attached to a barrier / backend
|
||||||
func mockRequest(t *testing.T, op Operation, path string) *Request {
|
func mockView(t *testing.T, prefix string) *BarrierView {
|
||||||
inm := physical.NewInmem()
|
inm := physical.NewInmem()
|
||||||
b, err := NewAESGCMBarrier(inm)
|
b, err := NewAESGCMBarrier(inm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -21,7 +21,13 @@ func mockRequest(t *testing.T, op Operation, path string) *Request {
|
|||||||
b.Unseal(key)
|
b.Unseal(key)
|
||||||
|
|
||||||
// Create the barrier view
|
// 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
|
// Create the request
|
||||||
req := &Request{
|
req := &Request{
|
||||||
|
|||||||
Reference in New Issue
Block a user