diff --git a/acl/acl.go b/acl/acl.go index e6d45d23e..071029603 100644 --- a/acl/acl.go +++ b/acl/acl.go @@ -5,29 +5,47 @@ import ( ) var ( - // allowAll is a singleton policy which allows all actions + // allowAll is a singleton policy which allows all + // non-management actions allowAll ACL // denyAll is a singleton policy which denies all actions denyAll ACL + + // manageAll is a singleton policy which allows all + // actions, including management + manageAll ACL ) func init() { // Setup the singletons - allowAll = &StaticACL{defaultAllow: true} - denyAll = &StaticACL{defaultAllow: false} + allowAll = &StaticACL{ + allowManage: false, + defaultAllow: true, + } + denyAll = &StaticACL{ + allowManage: false, + defaultAllow: false, + } + manageAll = &StaticACL{ + allowManage: true, + defaultAllow: true, + } } // ACL is the interface for policy enforcement. type ACL interface { KeyRead(string) bool KeyWrite(string) bool + ACLList() bool + ACLModify() bool } // StaticACL is used to implement a base ACL policy. It either // allows or denies all requests. This can be used as a parent // ACL to act in a blacklist or whitelist mode. type StaticACL struct { + allowManage bool defaultAllow bool } @@ -39,6 +57,14 @@ func (s *StaticACL) KeyWrite(string) bool { return s.defaultAllow } +func (s *StaticACL) ACLList() bool { + return s.allowManage +} + +func (s *StaticACL) ACLModify() bool { + return s.allowManage +} + // AllowAll returns an ACL rule that allows all operations func AllowAll() ACL { return allowAll @@ -49,6 +75,11 @@ func DenyAll() ACL { return denyAll } +// ManageAll returns an ACL rule that can manage all resources +func ManageAll() ACL { + return manageAll +} + // RootACL returns a possible ACL if the ID matches a root policy func RootACL(id string) ACL { switch id { @@ -56,6 +87,8 @@ func RootACL(id string) ACL { return allowAll case "deny": return denyAll + case "manage": + return manageAll default: return nil } @@ -122,3 +155,13 @@ func (p *PolicyACL) KeyWrite(key string) bool { // No matching rule, use the parent. return p.parent.KeyWrite(key) } + +// ACLList checks if listing of ACLs is allowed +func (p *PolicyACL) ACLList() bool { + return p.ACLList() +} + +// ACLModify checks if modification of ACLs is allowed +func (p *PolicyACL) ACLModify() bool { + return p.ACLModify() +} diff --git a/acl/acl_test.go b/acl/acl_test.go index 444b3bd7f..37cff4d07 100644 --- a/acl/acl_test.go +++ b/acl/acl_test.go @@ -11,6 +11,9 @@ func TestRootACL(t *testing.T) { if RootACL("deny") != DenyAll() { t.Fatalf("Bad root") } + if RootACL("manage") != ManageAll() { + t.Fatalf("Bad root") + } if RootACL("foo") != nil { t.Fatalf("bad root") } @@ -27,12 +30,23 @@ func TestStaticACL(t *testing.T) { t.Fatalf("expected static") } + manage := ManageAll() + if _, ok := none.(*StaticACL); !ok { + t.Fatalf("expected static") + } + if !all.KeyRead("foobar") { t.Fatalf("should allow") } if !all.KeyWrite("foobar") { t.Fatalf("should allow") } + if all.ACLList() { + t.Fatalf("should not allow") + } + if all.ACLModify() { + t.Fatalf("should not allow") + } if none.KeyRead("foobar") { t.Fatalf("should not allow") @@ -40,6 +54,25 @@ func TestStaticACL(t *testing.T) { if none.KeyWrite("foobar") { t.Fatalf("should not allow") } + if none.ACLList() { + t.Fatalf("should not noneow") + } + if none.ACLModify() { + t.Fatalf("should not noneow") + } + + if !manage.KeyRead("foobar") { + t.Fatalf("should allow") + } + if !manage.KeyWrite("foobar") { + t.Fatalf("should allow") + } + if !manage.ACLList() { + t.Fatalf("should allow") + } + if !manage.ACLModify() { + t.Fatalf("should allow") + } } func TestPolicyACL(t *testing.T) {