acl: Avoid shared cache with different parents

This commit is contained in:
Armon Dadgar 2014-08-14 19:32:05 -07:00
parent 7473bd2fc9
commit 88ee7b45cb
2 changed files with 60 additions and 7 deletions

View File

@ -13,9 +13,9 @@ type FaultFunc func(id string) (string, string, error)
// aclEntry allows us to store the ACL with it's policy ID // aclEntry allows us to store the ACL with it's policy ID
type aclEntry struct { type aclEntry struct {
ACL ACL ACL ACL
Parent string Parent string
PolicyID string RuleID string
} }
// Cache is used to implement policy and ACL caching // Cache is used to implement policy and ACL caching
@ -71,13 +71,18 @@ func (c *Cache) ruleID(rules string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(rules))) return fmt.Sprintf("%x", md5.Sum([]byte(rules)))
} }
// policyID returns the cache ID for a policy
func (c *Cache) policyID(parent, ruleID string) string {
return parent + ":" + ruleID
}
// GetACLPolicy is used to get the potentially cached ACL // GetACLPolicy is used to get the potentially cached ACL
// policy. If not cached, it will be generated and then cached. // policy. If not cached, it will be generated and then cached.
func (c *Cache) GetACLPolicy(id string) (string, *Policy, error) { func (c *Cache) GetACLPolicy(id string) (string, *Policy, error) {
// Check for a cached acl // Check for a cached acl
if raw, ok := c.aclCache.Get(id); ok { if raw, ok := c.aclCache.Get(id); ok {
cached := raw.(aclEntry) cached := raw.(aclEntry)
if raw, ok := c.ruleCache.Get(cached.PolicyID); ok { if raw, ok := c.ruleCache.Get(cached.RuleID); ok {
return cached.Parent, raw.(*Policy), nil return cached.Parent, raw.(*Policy), nil
} }
} }
@ -110,8 +115,9 @@ func (c *Cache) GetACL(id string) (ACL, error) {
ruleID := c.ruleID(rules) ruleID := c.ruleID(rules)
// Check for a compiled ACL // Check for a compiled ACL
policyID := c.policyID(parentID, ruleID)
var compiled ACL var compiled ACL
if raw, ok := c.policyCache.Get(ruleID); ok { if raw, ok := c.policyCache.Get(policyID); ok {
compiled = raw.(ACL) compiled = raw.(ACL)
} else { } else {
// Get the policy // Get the policy
@ -136,7 +142,7 @@ func (c *Cache) GetACL(id string) (ACL, error) {
} }
// Cache the compiled ACL // Cache the compiled ACL
c.policyCache.Add(ruleID, acl) c.policyCache.Add(policyID, acl)
compiled = acl compiled = acl
} }

View File

@ -114,7 +114,7 @@ func TestCache_ClearACL(t *testing.T) {
c.ClearACL("foo") c.ClearACL("foo")
// Clear the policy cache // Clear the policy cache
c.policyCache.Remove(c.ruleID(testSimplePolicy)) c.policyCache.Purge()
acl2, err := c.GetACL("foo") acl2, err := c.GetACL("foo")
if err != nil { if err != nil {
@ -238,6 +238,53 @@ func TestCache_GetACL_Parent(t *testing.T) {
} }
} }
func TestCache_GetACL_ParentCache(t *testing.T) {
// Same rules, different parent
faultfn := func(id string) (string, string, error) {
switch id {
case "foo":
return "allow", testSimplePolicy, nil
case "bar":
return "deny", testSimplePolicy, nil
}
t.Fatalf("bad case")
return "", "", nil
}
c, err := NewCache(16, 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")
}
acl2, err := c.GetACL("bar")
if err != nil {
t.Fatalf("err: %v", err)
}
if acl == acl2 {
t.Fatalf("should not match")
}
if acl2.KeyRead("bar/test") {
t.Fatalf("should not allow")
}
if !acl2.KeyRead("foo/test") {
t.Fatalf("should allow")
}
}
var testSimplePolicy = ` var testSimplePolicy = `
key "foo/" { key "foo/" {
policy = "read" policy = "read"