open-vault/vault/acl.go

110 lines
2.8 KiB
Go

package vault
import (
"github.com/armon/go-radix"
"github.com/hashicorp/vault/logical"
)
// operationPolicyLevel is used to map each logical operation
// into the minimum required permissions to allow the operation.
var operationPolicyLevel = map[logical.Operation]int{
logical.ReadOperation: pathPolicyLevel[PathPolicyRead],
logical.WriteOperation: pathPolicyLevel[PathPolicyWrite],
logical.DeleteOperation: pathPolicyLevel[PathPolicyWrite],
logical.ListOperation: pathPolicyLevel[PathPolicyRead],
logical.RevokeOperation: pathPolicyLevel[PathPolicyWrite],
logical.RenewOperation: pathPolicyLevel[PathPolicyRead],
logical.HelpOperation: pathPolicyLevel[PathPolicyDeny],
}
// ACL is used to wrap a set of policies to provide
// an efficient interface for access control.
type ACL struct {
// pathRules contains the path policies
pathRules *radix.Tree
// root is enabled if the "root" named policy is present.
root bool
}
// New is used to construct a policy based ACL from a set of policies.
func NewACL(policies []*Policy) (*ACL, error) {
// Initialize
a := &ACL{
pathRules: radix.New(),
root: false,
}
// Inject each policy
for _, policy := range policies {
// Ignore a nil policy object
if policy == nil {
continue
}
// Check if this is root
if policy.Name == "root" {
a.root = true
}
for _, pp := range policy.Paths {
// Convert to a policy level
policyLevel := pathPolicyLevel[pp.Policy]
// Check for an existing policy
raw, ok := a.pathRules.Get(pp.Prefix)
if !ok {
a.pathRules.Insert(pp.Prefix, policyLevel)
continue
}
existing := raw.(int)
// Check if this policy is a higher access level,
// we want to store the highest permission permitted.
if policyLevel > existing {
a.pathRules.Insert(pp.Prefix, policyLevel)
}
}
}
return a, nil
}
// AllowOperation is used to check if the given operation is permitted
func (a *ACL) AllowOperation(op logical.Operation, path string) bool {
// Fast-path root
if a.root {
return true
}
// Find a matching rule, default deny if no match
policyLevel := 0
_, rule, ok := a.pathRules.LongestPrefix(path)
if ok {
policyLevel = rule.(int)
}
// Convert the operation to a minimum required level
requiredLevel := operationPolicyLevel[op]
// Check if the minimum permissions are met
return policyLevel >= requiredLevel
}
// RootPrivilege checks if the user has root level permission
// to given path. This requires that the user be root, or that
// sudo privilege is available on that path.
func (a *ACL) RootPrivilege(path string) bool {
// Fast-path root
if a.root {
return true
}
// Check the rules for a match
_, rule, ok := a.pathRules.LongestPrefix(path)
if !ok {
return false
}
// Check the policy level
policyLevel := rule.(int)
return policyLevel == pathPolicyLevel[PathPolicySudo]
}