vault: Adding PolicyStore
This commit is contained in:
parent
061b6b24f1
commit
51ce336753
|
@ -83,7 +83,10 @@ func TestACL_Layered(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
testLayeredACL(t, acl)
|
||||
}
|
||||
|
||||
func testLayeredACL(t *testing.T, acl *ACL) {
|
||||
if acl.RootPrivilege("sys/mount/foo") {
|
||||
t.Fatalf("unexpected root")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// PolicyStore is used to provide durable storage of policy, and to
|
||||
// manage ACLs associated with them.
|
||||
type PolicyStore struct {
|
||||
view *BarrierView
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewPolicyStore creates a new PolicyStore that is backed
|
||||
// using a given view. It used used to durable store and manage named policy.
|
||||
func NewPolicyStore(view *BarrierView, logger *log.Logger) *PolicyStore {
|
||||
if logger == nil {
|
||||
logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
}
|
||||
p := &PolicyStore{
|
||||
view: view,
|
||||
logger: logger,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// SetPolicy is used to create or update the given policy
|
||||
func (ps *PolicyStore) SetPolicy(p *Policy) error {
|
||||
if p.Name == "root" {
|
||||
return fmt.Errorf("cannot update root policy")
|
||||
}
|
||||
if p.Name == "" {
|
||||
return fmt.Errorf("policy name missing")
|
||||
}
|
||||
|
||||
entry := &logical.StorageEntry{
|
||||
Key: p.Name,
|
||||
Value: []byte(p.Raw),
|
||||
}
|
||||
if err := ps.view.Put(entry); err != nil {
|
||||
return fmt.Errorf("failed to persist policy: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPolicy is used to fetch the named policy
|
||||
func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
|
||||
// TODO: Cache policy
|
||||
// Load the policy in
|
||||
out, err := ps.view.Get(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read policy: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Parse into a policy object
|
||||
p, err := Parse(string(out.Value))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse policy: %v", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ListPolicies is used to list the available policies
|
||||
func (ps *PolicyStore) ListPolicies() ([]string, error) {
|
||||
// Scan the view, since the policy names are the same as the
|
||||
// key names.
|
||||
return CollectKeys(ps.view)
|
||||
}
|
||||
|
||||
// DeletePolicy is used to delete the named policy
|
||||
func (ps *PolicyStore) DeletePolicy(name string) error {
|
||||
if err := ps.view.Delete(name); err != nil {
|
||||
return fmt.Errorf("failed to delete policy: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ACL is used to return an ACL which is built using the
|
||||
// named policies.
|
||||
func (ps *PolicyStore) ACL(names ...string) (*ACL, error) {
|
||||
// TODO: Cache ACLs
|
||||
// Fetch the policies
|
||||
var policy []*Policy
|
||||
for _, name := range names {
|
||||
p, err := ps.GetPolicy(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get policy '%s': %v", name, err)
|
||||
}
|
||||
policy = append(policy, p)
|
||||
}
|
||||
|
||||
// Construct the ACL
|
||||
acl, err := NewACL(policy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct ACL: %v", err)
|
||||
}
|
||||
return acl, nil
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mockPolicyStore(t *testing.T) *PolicyStore {
|
||||
_, barrier, _ := mockBarrier(t)
|
||||
view := NewBarrierView(barrier, "foo/")
|
||||
p := NewPolicyStore(view, nil)
|
||||
return p
|
||||
}
|
||||
|
||||
func TestPolicyStore_CRUD(t *testing.T) {
|
||||
ps := mockPolicyStore(t)
|
||||
|
||||
// Get should return nothing
|
||||
p, err := ps.GetPolicy("dev")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if p != nil {
|
||||
t.Fatalf("bad: %v", p)
|
||||
}
|
||||
|
||||
// Delete should be no-op
|
||||
err = ps.DeletePolicy("dev")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// List should be blank
|
||||
out, err := ps.ListPolicies()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
|
||||
// Set should work
|
||||
policy, _ := Parse(aclPolicy)
|
||||
err = ps.SetPolicy(policy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Get should work
|
||||
p, err = ps.GetPolicy("dev")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(p, policy) {
|
||||
t.Fatalf("bad: %v", p)
|
||||
}
|
||||
|
||||
// List should be one element
|
||||
out, err = ps.ListPolicies()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if len(out) != 1 || out[0] != "dev" {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
|
||||
// Delete should be clear the entry
|
||||
err = ps.DeletePolicy("dev")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Get should fail
|
||||
p, err = ps.GetPolicy("dev")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if p != nil {
|
||||
t.Fatalf("bad: %v", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyStore_ACL(t *testing.T) {
|
||||
ps := mockPolicyStore(t)
|
||||
|
||||
policy, _ := Parse(aclPolicy)
|
||||
err := ps.SetPolicy(policy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
policy, _ = Parse(aclPolicy2)
|
||||
err = ps.SetPolicy(policy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
acl, err := ps.ACL("dev", "ops")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
testLayeredACL(t, acl)
|
||||
}
|
Loading…
Reference in New Issue