consul/acl/acl.go

278 lines
5.9 KiB
Go
Raw Normal View History

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
// non-management actions
2014-08-06 22:08:17 +00:00
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
2014-08-06 22:08:17 +00:00
)
func init() {
// Setup the singletons
allowAll = &StaticACL{
allowManage: false,
defaultAllow: true,
}
denyAll = &StaticACL{
allowManage: false,
defaultAllow: false,
}
manageAll = &StaticACL{
allowManage: true,
defaultAllow: true,
}
2014-08-06 22:08:17 +00:00
}
// ACL is the interface for policy enforcement.
type ACL interface {
// KeyRead checks for permission to read a given key
2014-08-06 22:08:17 +00:00
KeyRead(string) bool
// KeyWrite checks for permission to write a given key
2014-08-06 22:08:17 +00:00
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
2014-12-01 03:33:46 +00:00
// ServiceWrite checks for permission to read a given service
ServiceWrite(string) bool
// ServiceRead checks for permission to read a given service
ServiceRead(string) bool
// ACLList checks for permission to list all the ACLs
ACLList() bool
// ACLModify checks for permission to manipulate ACLs
ACLModify() bool
2014-08-06 22:08:17 +00:00
}
// 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
2014-08-06 22:08:17 +00:00
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
}
2014-12-01 03:33:46 +00:00
func (s *StaticACL) ServiceRead(string) bool {
return s.defaultAllow
}
func (s *StaticACL) ServiceWrite(string) bool {
return s.defaultAllow
}
func (s *StaticACL) ACLList() bool {
return s.allowManage
}
func (s *StaticACL) ACLModify() bool {
return s.allowManage
}
2014-08-06 22:08:17 +00:00
// 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
}
}
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
// keyRules contains the key policies
keyRules *radix.Tree
2014-12-01 03:33:46 +00:00
// serviceRules contains the service policies
2015-05-05 06:25:19 +00:00
serviceRules *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{
2014-12-01 03:33:46 +00:00
parent: parent,
keyRules: radix.New(),
2015-05-05 06:25:19 +00:00
serviceRules: radix.New(),
2014-08-06 22:08:17 +00:00
}
// Load the key policy
for _, kp := range policy.Keys {
p.keyRules.Insert(kp.Prefix, kp.Policy)
2014-08-06 22:08:17 +00:00
}
2014-12-01 03:33:46 +00:00
// Load the service policy
for _, sp := range policy.Services {
2015-05-05 06:25:19 +00:00
p.serviceRules.Insert(sp.Name, sp.Policy)
2014-12-01 03:33:46 +00:00
}
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
_, rule, ok := p.keyRules.LongestPrefix(key)
2014-08-06 22:08:17 +00:00
if ok {
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
_, rule, ok := p.keyRules.LongestPrefix(key)
2014-08-06 22:08:17 +00:00
if ok {
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)
}
// 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)
}
2014-12-01 03:33:46 +00:00
// ServiceRead checks if reading (discovery) of a service is allowed
func (p *PolicyACL) ServiceRead(name string) bool {
// Check for an exact rule or catch-all
2015-05-05 06:25:19 +00:00
_, rule, ok := p.serviceRules.LongestPrefix(name)
2014-12-01 03:33:46 +00:00
if ok {
switch rule {
case ServicePolicyWrite:
return true
case ServicePolicyRead:
return true
default:
return false
}
}
// No matching rule, use the parent.
return p.parent.ServiceRead(name)
}
// ServiceWrite checks if writing (registering) a service is allowed
func (p *PolicyACL) ServiceWrite(name string) bool {
// Check for an exact rule or catch-all
2015-05-05 06:25:19 +00:00
_, rule, ok := p.serviceRules.LongestPrefix(name)
2014-12-01 03:33:46 +00:00
if ok {
switch rule {
case ServicePolicyWrite:
return true
default:
return false
}
}
// No matching rule, use the parent.
return p.parent.ServiceWrite(name)
}
// ACLList checks if listing of ACLs is allowed
func (p *PolicyACL) ACLList() bool {
2014-08-12 22:10:45 +00:00
return p.parent.ACLList()
}
// ACLModify checks if modification of ACLs is allowed
func (p *PolicyACL) ACLModify() bool {
2014-08-12 22:10:45 +00:00
return p.parent.ACLModify()
}