Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates new "prepared-query" ACL type and new token capture behavior. #1748

Merged
merged 9 commits into from
Feb 25, 2016
123 changes: 78 additions & 45 deletions acl/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ type ACL interface {
// EventWrite determines if a specific event may be fired.
EventWrite(string) bool

// PrepardQueryRead determines if a specific prepared query can be read
// to show its contents (this is not used for execution).
PreparedQueryRead(string) bool

// PreparedQueryWrite determines if a specific prepared query can be
// created, modified, or deleted.
PreparedQueryWrite(string) bool

// KeyringRead determines if the encryption keyring used in
// the gossip layer can be read.
KeyringRead() bool
Expand All @@ -70,12 +78,6 @@ type ACL interface {

// ACLModify checks for permission to manipulate ACLs
ACLModify() bool

// QueryList checks for permission to list all the prepared queries.
QueryList() bool

// QueryModify checks for permission to modify any prepared query.
QueryModify() bool
}

// StaticACL is used to implement a base ACL policy. It either
Expand Down Expand Up @@ -114,27 +116,27 @@ func (s *StaticACL) EventWrite(string) bool {
return s.defaultAllow
}

func (s *StaticACL) KeyringRead() bool {
func (s *StaticACL) PreparedQueryRead(string) bool {
return s.defaultAllow
}

func (s *StaticACL) KeyringWrite() bool {
func (s *StaticACL) PreparedQueryWrite(string) bool {
return s.defaultAllow
}

func (s *StaticACL) ACLList() bool {
return s.allowManage
func (s *StaticACL) KeyringRead() bool {
return s.defaultAllow
}

func (s *StaticACL) ACLModify() bool {
return s.allowManage
func (s *StaticACL) KeyringWrite() bool {
return s.defaultAllow
}

func (s *StaticACL) QueryList() bool {
func (s *StaticACL) ACLList() bool {
return s.allowManage
}

func (s *StaticACL) QueryModify() bool {
func (s *StaticACL) ACLModify() bool {
return s.allowManage
}

Expand Down Expand Up @@ -183,6 +185,9 @@ type PolicyACL struct {
// eventRules contains the user event policies
eventRules *radix.Tree

// preparedQueryRules contains the prepared query policies
preparedQueryRules *radix.Tree

// keyringRules contains the keyring policies. The keyring has
// a very simple yes/no without prefix matching, so here we
// don't need to use a radix tree.
Expand All @@ -193,10 +198,11 @@ type PolicyACL struct {
// and a parent policy to resolve missing cases.
func New(parent ACL, policy *Policy) (*PolicyACL, error) {
p := &PolicyACL{
parent: parent,
keyRules: radix.New(),
serviceRules: radix.New(),
eventRules: radix.New(),
parent: parent,
keyRules: radix.New(),
serviceRules: radix.New(),
eventRules: radix.New(),
preparedQueryRules: radix.New(),
}

// Load the key policy
Expand All @@ -214,6 +220,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
p.eventRules.Insert(ep.Event, ep.Policy)
}

// Load the prepared query policy
for _, pq := range policy.PreparedQueries {
p.preparedQueryRules.Insert(pq.Prefix, pq.Policy)
}

// Load the keyring policy
p.keyringRule = policy.Keyring

Expand All @@ -226,9 +237,7 @@ func (p *PolicyACL) KeyRead(key string) bool {
_, rule, ok := p.keyRules.LongestPrefix(key)
if ok {
switch rule.(string) {
case KeyPolicyRead:
return true
case KeyPolicyWrite:
case PolicyRead, PolicyWrite:
return true
default:
return false
Expand All @@ -245,7 +254,7 @@ func (p *PolicyACL) KeyWrite(key string) bool {
_, rule, ok := p.keyRules.LongestPrefix(key)
if ok {
switch rule.(string) {
case KeyPolicyWrite:
case PolicyWrite:
return true
default:
return false
Expand All @@ -260,15 +269,15 @@ func (p *PolicyACL) KeyWrite(key string) bool {
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 {
if ok && rule.(string) != PolicyWrite {
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 {
if rule.(string) != PolicyWrite {
deny = true
return true
}
Expand Down Expand Up @@ -296,9 +305,7 @@ func (p *PolicyACL) ServiceRead(name string) bool {

if ok {
switch rule {
case ServicePolicyWrite:
return true
case ServicePolicyRead:
case PolicyRead, PolicyWrite:
return true
default:
return false
Expand All @@ -316,7 +323,7 @@ func (p *PolicyACL) ServiceWrite(name string) bool {

if ok {
switch rule {
case ServicePolicyWrite:
case PolicyWrite:
return true
default:
return false
Expand All @@ -333,9 +340,7 @@ func (p *PolicyACL) EventRead(name string) bool {
// Longest-prefix match on event names
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
switch rule {
case EventPolicyRead:
return true
case EventPolicyWrite:
case PolicyRead, PolicyWrite:
return true
default:
return false
Expand All @@ -351,20 +356,58 @@ func (p *PolicyACL) EventRead(name string) bool {
func (p *PolicyACL) EventWrite(name string) bool {
// Longest-prefix match event names
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
return rule == EventPolicyWrite
return rule == PolicyWrite
}

// No match, use parent
return p.parent.EventWrite(name)
}

// PreparedQueryRead checks if reading (listing) of a prepared query is
// allowed - this isn't execution, just listing its contents.
func (p *PolicyACL) PreparedQueryRead(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.preparedQueryRules.LongestPrefix(prefix)

if ok {
switch rule {
case PolicyRead, PolicyWrite:
return true
default:
return false
}
}

// No matching rule, use the parent.
return p.parent.PreparedQueryRead(prefix)
}

// PreparedQueryWrite checks if writing (creating, updating, or deleting) of a
// prepared query is allowed.
func (p *PolicyACL) PreparedQueryWrite(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.preparedQueryRules.LongestPrefix(prefix)

if ok {
switch rule {
case PolicyWrite:
return true
default:
return false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit picking: seems like the switch isn't necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - this kind of looks like the rest of this file - might keep the switch for this one :-)

}
}

// No matching rule, use the parent.
return p.parent.PreparedQueryWrite(prefix)
}

// KeyringRead is used to determine if the keyring can be
// read by the current ACL token.
func (p *PolicyACL) KeyringRead() bool {
switch p.keyringRule {
case KeyringPolicyRead, KeyringPolicyWrite:
case PolicyRead, PolicyWrite:
return true
case KeyringPolicyDeny:
case PolicyDeny:
return false
default:
return p.parent.KeyringRead()
Expand All @@ -373,7 +416,7 @@ func (p *PolicyACL) KeyringRead() bool {

// KeyringWrite determines if the keyring can be manipulated.
func (p *PolicyACL) KeyringWrite() bool {
if p.keyringRule == KeyringPolicyWrite {
if p.keyringRule == PolicyWrite {
return true
}
return p.parent.KeyringWrite()
Expand All @@ -388,13 +431,3 @@ func (p *PolicyACL) ACLList() bool {
func (p *PolicyACL) ACLModify() bool {
return p.parent.ACLModify()
}

// QueryList checks if listing of all prepared queries is allowed.
func (p *PolicyACL) QueryList() bool {
return p.parent.QueryList()
}

// QueryModify checks if modifying of any prepared query is allowed.
func (p *PolicyACL) QueryModify() bool {
return p.parent.QueryModify()
}
Loading