mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
4.5 KiB
216 lines
4.5 KiB
package acl |
|
|
|
import ( |
|
"github.com/armon/go-radix" |
|
) |
|
|
|
var ( |
|
// 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{ |
|
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 checks for permission to read a given key |
|
KeyRead(string) bool |
|
|
|
// KeyWrite checks for permission to write a given key |
|
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 |
|
|
|
// ACLModify checks for permission to manipulate ACLs |
|
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 |
|
} |
|
|
|
func (s *StaticACL) KeyRead(string) bool { |
|
return s.defaultAllow |
|
} |
|
|
|
func (s *StaticACL) KeyWrite(string) bool { |
|
return s.defaultAllow |
|
} |
|
|
|
func (s *StaticACL) KeyWritePrefix(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 |
|
} |
|
|
|
// DenyAll returns an ACL rule that denies all operations |
|
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 { |
|
case "allow": |
|
return allowAll |
|
case "deny": |
|
return denyAll |
|
case "manage": |
|
return manageAll |
|
default: |
|
return nil |
|
} |
|
} |
|
|
|
// PolicyACL is used to wrap a set of ACL policies to provide |
|
// the ACL interface. |
|
type PolicyACL struct { |
|
// parent is used to resolve policy if we have |
|
// no matching rule. |
|
parent ACL |
|
|
|
// keyRules contains the key policies |
|
keyRules *radix.Tree |
|
} |
|
|
|
// New is used to construct a policy based ACL from a set of policies |
|
// and a parent policy to resolve missing cases. |
|
func New(parent ACL, policy *Policy) (*PolicyACL, error) { |
|
p := &PolicyACL{ |
|
parent: parent, |
|
keyRules: radix.New(), |
|
} |
|
|
|
// Load the key policy |
|
for _, kp := range policy.Keys { |
|
p.keyRules.Insert(kp.Prefix, kp.Policy) |
|
} |
|
return p, nil |
|
} |
|
|
|
// KeyRead returns if a key is allowed to be read |
|
func (p *PolicyACL) KeyRead(key string) bool { |
|
// Look for a matching rule |
|
_, rule, ok := p.keyRules.LongestPrefix(key) |
|
if ok { |
|
switch rule.(string) { |
|
case KeyPolicyRead: |
|
return true |
|
case KeyPolicyWrite: |
|
return true |
|
default: |
|
return false |
|
} |
|
} |
|
|
|
// No matching rule, use the parent. |
|
return p.parent.KeyRead(key) |
|
} |
|
|
|
// KeyWrite returns if a key is allowed to be written |
|
func (p *PolicyACL) KeyWrite(key string) bool { |
|
// Look for a matching rule |
|
_, rule, ok := p.keyRules.LongestPrefix(key) |
|
if ok { |
|
switch rule.(string) { |
|
case KeyPolicyWrite: |
|
return true |
|
default: |
|
return false |
|
} |
|
} |
|
|
|
// No matching rule, use the parent. |
|
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 |
|
func (p *PolicyACL) ACLList() bool { |
|
return p.parent.ACLList() |
|
} |
|
|
|
// ACLModify checks if modification of ACLs is allowed |
|
func (p *PolicyACL) ACLModify() bool { |
|
return p.parent.ACLModify() |
|
}
|
|
|