106 lines
2.7 KiB
Go
106 lines
2.7 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 {
|
||
|
// 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]
|
||
|
}
|