-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #291 from hashicorp/f-acl
Adding support for ACL system
- Loading branch information
Showing
36 changed files
with
4,526 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
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() | ||
} |
Oops, something went wrong.