mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +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
	 Armon Dadgar
					Armon Dadgar