Skip to content

Commit

Permalink
Merge pull request #1748 from hashicorp/f-client-pq-acls
Browse files Browse the repository at this point in the history
Creates new "prepared-query" ACL type and new token capture behavior.
  • Loading branch information
slackpad committed Feb 25, 2016
2 parents 2b2c1a5 + dca480e commit ad77f20
Show file tree
Hide file tree
Showing 15 changed files with 1,109 additions and 484 deletions.
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
}
}

// 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

0 comments on commit ad77f20

Please sign in to comment.