2014-08-06 22:08:17 +00:00
|
|
|
package acl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/armon/go-radix"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// allowAll is a singleton policy which allows all actions
|
|
|
|
allowAll ACL
|
|
|
|
|
|
|
|
// denyAll is a singleton policy which denies all actions
|
|
|
|
denyAll ACL
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Setup the singletons
|
|
|
|
allowAll = &StaticACL{defaultAllow: true}
|
|
|
|
denyAll = &StaticACL{defaultAllow: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACL is the interface for policy enforcement.
|
|
|
|
type ACL interface {
|
|
|
|
KeyRead(string) bool
|
|
|
|
KeyWrite(string) 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 {
|
|
|
|
defaultAllow bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StaticACL) KeyRead(string) bool {
|
|
|
|
return s.defaultAllow
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StaticACL) KeyWrite(string) bool {
|
|
|
|
return s.defaultAllow
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2014-08-12 17:35:27 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-06 22:08:17 +00:00
|
|
|
// 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
|
|
|
|
|
2014-08-11 05:01:03 +00:00
|
|
|
// keyRules contains the key policies
|
|
|
|
keyRules *radix.Tree
|
2014-08-06 22:08:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
2014-08-11 05:01:03 +00:00
|
|
|
keyRules: radix.New(),
|
2014-08-06 22:08:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the key policy
|
|
|
|
for _, kp := range policy.Keys {
|
2014-08-11 05:01:03 +00:00
|
|
|
p.keyRules.Insert(kp.Prefix, kp.Policy)
|
2014-08-06 22:08:17 +00:00
|
|
|
}
|
|
|
|
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
|
2014-08-11 05:01:03 +00:00
|
|
|
_, rule, ok := p.keyRules.LongestPrefix(key)
|
2014-08-06 22:08:17 +00:00
|
|
|
if ok {
|
2014-08-11 05:01:03 +00:00
|
|
|
switch rule.(string) {
|
|
|
|
case KeyPolicyRead:
|
|
|
|
return true
|
|
|
|
case KeyPolicyWrite:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
2014-08-06 22:08:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2014-08-11 05:01:03 +00:00
|
|
|
_, rule, ok := p.keyRules.LongestPrefix(key)
|
2014-08-06 22:08:17 +00:00
|
|
|
if ok {
|
2014-08-11 05:01:03 +00:00
|
|
|
switch rule.(string) {
|
|
|
|
case KeyPolicyWrite:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
2014-08-06 22:08:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// No matching rule, use the parent.
|
|
|
|
return p.parent.KeyWrite(key)
|
|
|
|
}
|