forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
acl.go
109 lines (94 loc) · 2.81 KB
/
acl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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]
}