From 5c0da3a4d7dadc086466d750a459f83860cd7dd9 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Tue, 12 Aug 2014 10:35:27 -0700 Subject: [PATCH] acl: Simplify parent ACL, adding root policies --- acl/acl.go | 12 ++++++++++ acl/acl_test.go | 12 ++++++++++ acl/cache.go | 29 +++++++++++++++--------- acl/cache_test.go | 57 ++++++++++++++++++++++++++++++++++++----------- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/acl/acl.go b/acl/acl.go index f0e3c83552..e6d45d23e0 100644 --- a/acl/acl.go +++ b/acl/acl.go @@ -49,6 +49,18 @@ func DenyAll() ACL { return denyAll } +// RootACL returns a possible ACL if the ID matches a root policy +func RootACL(id string) ACL { + switch id { + case "allow": + return allowAll + case "deny": + return denyAll + default: + return nil + } +} + // PolicyACL is used to wrap a set of ACL policies to provide // the ACL interface. type PolicyACL struct { diff --git a/acl/acl_test.go b/acl/acl_test.go index baf327e092..444b3bd7fe 100644 --- a/acl/acl_test.go +++ b/acl/acl_test.go @@ -4,6 +4,18 @@ import ( "testing" ) +func TestRootACL(t *testing.T) { + if RootACL("allow") != AllowAll() { + t.Fatalf("Bad root") + } + if RootACL("deny") != DenyAll() { + t.Fatalf("Bad root") + } + if RootACL("foo") != nil { + t.Fatalf("bad root") + } +} + func TestStaticACL(t *testing.T) { all := AllowAll() if _, ok := all.(*StaticACL); !ok { diff --git a/acl/cache.go b/acl/cache.go index b61e32fc6f..2b1409ea98 100644 --- a/acl/cache.go +++ b/acl/cache.go @@ -7,9 +7,9 @@ import ( "github.com/hashicorp/golang-lru" ) -// FaultFunc is a function used to fault in the rules for an -// ACL given it's ID -type FaultFunc func(id string) (string, error) +// FaultFunc is a function used to fault in the parent, +// rules for an ACL given it's ID +type FaultFunc func(id string) (string, string, error) // aclEntry allows us to store the ACL with it's policy ID type aclEntry struct { @@ -19,15 +19,14 @@ type aclEntry struct { // Cache is used to implement policy and ACL caching type Cache struct { - aclCache *lru.Cache // Cache id -> acl faultfn FaultFunc - parent ACL + aclCache *lru.Cache // Cache id -> acl policyCache *lru.Cache // Cache policy -> acl ruleCache *lru.Cache // Cache rules -> policy } // NewCache contructs a new policy and ACL cache of a given size -func NewCache(size int, parent ACL, faultfn FaultFunc) (*Cache, error) { +func NewCache(size int, faultfn FaultFunc) (*Cache, error) { if size <= 0 { return nil, fmt.Errorf("Must provide positive cache size") } @@ -35,9 +34,8 @@ func NewCache(size int, parent ACL, faultfn FaultFunc) (*Cache, error) { pc, _ := lru.New(size) ac, _ := lru.New(size) c := &Cache{ - aclCache: ac, faultfn: faultfn, - parent: parent, + aclCache: ac, policyCache: pc, ruleCache: rc, } @@ -84,7 +82,7 @@ func (c *Cache) GetACLPolicy(id string) (*Policy, error) { } // Fault in the rules - rules, err := c.faultfn(id) + _, rules, err := c.faultfn(id) if err != nil { return nil, err } @@ -103,7 +101,7 @@ func (c *Cache) GetACL(id string) (ACL, error) { } // Get the rules - rules, err := c.faultfn(id) + parentID, rules, err := c.faultfn(id) if err != nil { return nil, err } @@ -120,8 +118,17 @@ func (c *Cache) GetACL(id string) (ACL, error) { return nil, err } + // Get the parent ACL + parent := RootACL(parentID) + if parent == nil { + parent, err = c.GetACL(parentID) + if err != nil { + return nil, err + } + } + // Compile the ACL - acl, err := New(c.parent, policy) + acl, err := New(parent, policy) if err != nil { return nil, err } diff --git a/acl/cache_test.go b/acl/cache_test.go index 51ca5a2dfe..96b06ba34b 100644 --- a/acl/cache_test.go +++ b/acl/cache_test.go @@ -5,7 +5,7 @@ import ( ) func TestCache_GetPolicy(t *testing.T) { - c, err := NewCache(1, AllowAll(), nil) + c, err := NewCache(1, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -45,11 +45,11 @@ func TestCache_GetACL(t *testing.T) { "foo": testSimplePolicy, "bar": testSimplePolicy2, } - faultfn := func(id string) (string, error) { - return policies[id], nil + faultfn := func(id string) (string, string, error) { + return "deny", policies[id], nil } - c, err := NewCache(1, DenyAll(), faultfn) + c, err := NewCache(1, faultfn) if err != nil { t.Fatalf("err: %v", err) } @@ -96,11 +96,11 @@ func TestCache_ClearACL(t *testing.T) { "foo": testSimplePolicy, "bar": testSimplePolicy, } - faultfn := func(id string) (string, error) { - return policies[id], nil + faultfn := func(id string) (string, string, error) { + return "deny", policies[id], nil } - c, err := NewCache(1, DenyAll(), faultfn) + c, err := NewCache(1, faultfn) if err != nil { t.Fatalf("err: %v", err) } @@ -131,11 +131,11 @@ func TestCache_Purge(t *testing.T) { "foo": testSimplePolicy, "bar": testSimplePolicy, } - faultfn := func(id string) (string, error) { - return policies[id], nil + faultfn := func(id string) (string, string, error) { + return "deny", policies[id], nil } - c, err := NewCache(1, DenyAll(), faultfn) + c, err := NewCache(1, faultfn) if err != nil { t.Fatalf("err: %v", err) } @@ -164,10 +164,10 @@ func TestCache_GetACLPolicy(t *testing.T) { "foo": testSimplePolicy, "bar": testSimplePolicy, } - faultfn := func(id string) (string, error) { - return policies[id], nil + faultfn := func(id string) (string, string, error) { + return "deny", policies[id], nil } - c, err := NewCache(1, DenyAll(), faultfn) + c, err := NewCache(1, faultfn) if err != nil { t.Fatalf("err: %v", err) } @@ -201,6 +201,37 @@ func TestCache_GetACLPolicy(t *testing.T) { } } +func TestCache_GetACL_Parent(t *testing.T) { + faultfn := func(id string) (string, string, error) { + switch id { + case "foo": + // Foo inherits from bar + return "bar", testSimplePolicy, nil + case "bar": + return "deny", testSimplePolicy2, nil + } + t.Fatalf("bad case") + return "", "", nil + } + + c, err := NewCache(1, faultfn) + if err != nil { + t.Fatalf("err: %v", err) + } + + acl, err := c.GetACL("foo") + if err != nil { + t.Fatalf("err: %v", err) + } + + if !acl.KeyRead("bar/test") { + t.Fatalf("should allow") + } + if !acl.KeyRead("foo/test") { + t.Fatalf("should allow") + } +} + var testSimplePolicy = ` key "foo/" { policy = "read"