acl: Support checking write permissions on a prefix
This commit is contained in:
parent
ea015710e9
commit
f3336fc732
49
acl/acl.go
49
acl/acl.go
|
@ -35,9 +35,21 @@ func init() {
|
||||||
|
|
||||||
// ACL is the interface for policy enforcement.
|
// ACL is the interface for policy enforcement.
|
||||||
type ACL interface {
|
type ACL interface {
|
||||||
|
// KeyRead checks for permission to read a given key
|
||||||
KeyRead(string) bool
|
KeyRead(string) bool
|
||||||
|
|
||||||
|
// KeyWrite checks for permission to write a given key
|
||||||
KeyWrite(string) bool
|
KeyWrite(string) bool
|
||||||
|
|
||||||
|
// KeyWritePrefix checks for permission to write to an
|
||||||
|
// entire key prefix. This means there must be no sub-policies
|
||||||
|
// that deny a write.
|
||||||
|
KeyWritePrefix(string) bool
|
||||||
|
|
||||||
|
// ACLList checks for permission to list all the ACLs
|
||||||
ACLList() bool
|
ACLList() bool
|
||||||
|
|
||||||
|
// ACLModify checks for permission to manipulate ACLs
|
||||||
ACLModify() bool
|
ACLModify() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +69,10 @@ func (s *StaticACL) KeyWrite(string) bool {
|
||||||
return s.defaultAllow
|
return s.defaultAllow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StaticACL) KeyWritePrefix(string) bool {
|
||||||
|
return s.defaultAllow
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StaticACL) ACLList() bool {
|
func (s *StaticACL) ACLList() bool {
|
||||||
return s.allowManage
|
return s.allowManage
|
||||||
}
|
}
|
||||||
|
@ -156,6 +172,39 @@ func (p *PolicyACL) KeyWrite(key string) bool {
|
||||||
return p.parent.KeyWrite(key)
|
return p.parent.KeyWrite(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KeyWritePrefix returns if a prefix is allowed to be written
|
||||||
|
func (p *PolicyACL) KeyWritePrefix(prefix string) bool {
|
||||||
|
// Look for a matching rule that denies
|
||||||
|
_, rule, ok := p.keyRules.LongestPrefix(prefix)
|
||||||
|
if ok && rule.(string) != KeyPolicyWrite {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look if any of our children have a deny policy
|
||||||
|
deny := false
|
||||||
|
p.keyRules.WalkPrefix(prefix, func(path string, rule interface{}) bool {
|
||||||
|
// We have a rule to prevent a write in a sub-directory!
|
||||||
|
if rule.(string) != KeyPolicyWrite {
|
||||||
|
deny = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Deny the write if any sub-rules may be violated
|
||||||
|
if deny {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we had a matching rule, done
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matching rule, use the parent.
|
||||||
|
return p.parent.KeyWritePrefix(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
// ACLList checks if listing of ACLs is allowed
|
// ACLList checks if listing of ACLs is allowed
|
||||||
func (p *PolicyACL) ACLList() bool {
|
func (p *PolicyACL) ACLList() bool {
|
||||||
return p.parent.ACLList()
|
return p.parent.ACLList()
|
||||||
|
|
|
@ -103,16 +103,19 @@ func TestPolicyACL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcase struct {
|
type tcase struct {
|
||||||
inp string
|
inp string
|
||||||
read bool
|
read bool
|
||||||
write bool
|
write bool
|
||||||
|
writePrefix bool
|
||||||
}
|
}
|
||||||
cases := []tcase{
|
cases := []tcase{
|
||||||
{"other", true, true},
|
{"other", true, true, true},
|
||||||
{"foo/test", true, true},
|
{"foo/test", true, true, true},
|
||||||
{"foo/priv/test", false, false},
|
{"foo/priv/test", false, false, false},
|
||||||
{"bar/any", false, false},
|
{"bar/any", false, false, false},
|
||||||
{"zip/test", true, false},
|
{"zip/test", true, false, false},
|
||||||
|
{"foo/", true, true, false},
|
||||||
|
{"", true, true, false},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
if c.read != acl.KeyRead(c.inp) {
|
if c.read != acl.KeyRead(c.inp) {
|
||||||
|
@ -121,6 +124,9 @@ func TestPolicyACL(t *testing.T) {
|
||||||
if c.write != acl.KeyWrite(c.inp) {
|
if c.write != acl.KeyWrite(c.inp) {
|
||||||
t.Fatalf("Write fail: %#v", c)
|
t.Fatalf("Write fail: %#v", c)
|
||||||
}
|
}
|
||||||
|
if c.writePrefix != acl.KeyWritePrefix(c.inp) {
|
||||||
|
t.Fatalf("Write prefix fail: %#v", c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,16 +171,17 @@ func TestPolicyACL_Parent(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcase struct {
|
type tcase struct {
|
||||||
inp string
|
inp string
|
||||||
read bool
|
read bool
|
||||||
write bool
|
write bool
|
||||||
|
writePrefix bool
|
||||||
}
|
}
|
||||||
cases := []tcase{
|
cases := []tcase{
|
||||||
{"other", false, false},
|
{"other", false, false, false},
|
||||||
{"foo/test", true, true},
|
{"foo/test", true, true, true},
|
||||||
{"foo/priv/test", true, false},
|
{"foo/priv/test", true, false, false},
|
||||||
{"bar/any", false, false},
|
{"bar/any", false, false, false},
|
||||||
{"zip/test", true, false},
|
{"zip/test", true, false, false},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
if c.read != acl.KeyRead(c.inp) {
|
if c.read != acl.KeyRead(c.inp) {
|
||||||
|
@ -183,5 +190,8 @@ func TestPolicyACL_Parent(t *testing.T) {
|
||||||
if c.write != acl.KeyWrite(c.inp) {
|
if c.write != acl.KeyWrite(c.inp) {
|
||||||
t.Fatalf("Write fail: %#v", c)
|
t.Fatalf("Write fail: %#v", c)
|
||||||
}
|
}
|
||||||
|
if c.writePrefix != acl.KeyWritePrefix(c.inp) {
|
||||||
|
t.Fatalf("Write prefix fail: %#v", c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue