forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
acl.go
151 lines (132 loc) · 3.51 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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package vault
import (
"github.com/armon/go-radix"
"github.com/hashicorp/vault/logical"
)
var (
// Policy lists are used to restrict what is eligible for an operation
anyPolicy = []string{PathPolicyDeny}
readWriteSudo = []string{PathPolicyRead, PathPolicyWrite, PathPolicySudo}
writeSudo = []string{PathPolicyWrite, PathPolicySudo}
// permittedPolicyLevel is used to map each logical operation
// into the set of policies that allow the operation.
permittedPolicyLevels = map[logical.Operation][]string{
logical.ReadOperation: readWriteSudo,
logical.WriteOperation: writeSudo,
logical.DeleteOperation: writeSudo,
logical.ListOperation: readWriteSudo,
logical.HelpOperation: anyPolicy,
logical.RevokeOperation: writeSudo,
logical.RenewOperation: writeSudo,
logical.RollbackOperation: writeSudo,
}
)
// ACL is used to wrap a set of policies to provide
// an efficient interface for access control.
type ACL struct {
// exactRules contains the path policies that are exact
exactRules *radix.Tree
// globRules contains the path policies that glob
globRules *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{
exactRules: radix.New(),
globRules: 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 {
// Check which tree to use
tree := a.exactRules
if pp.Glob {
tree = a.globRules
}
// Check for an existing policy
raw, ok := tree.Get(pp.Prefix)
if !ok {
tree.Insert(pp.Prefix, pp)
continue
}
existing := raw.(*PathPolicy)
// Check if this policy is takes precedence
if pp.TakesPrecedence(existing) {
tree.Insert(pp.Prefix, pp)
}
}
}
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
}
// Check if any policy level allows this operation
permitted := permittedPolicyLevels[op]
if permitted[0] == PathPolicyDeny {
return true
}
// Find an exact matching rule, look for glob if no match
var policy *PathPolicy
raw, ok := a.exactRules.Get(path)
if ok {
policy = raw.(*PathPolicy)
goto CHECK
}
// Find a glob rule, default deny if no match
_, raw, ok = a.globRules.LongestPrefix(path)
if !ok {
return false
} else {
policy = raw.(*PathPolicy)
}
CHECK:
// Check if the minimum permissions are met
for _, allowed := range permitted {
if allowed == policy.Policy {
return true
}
}
return false
}
// 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
}
// Find an exact matching rule, look for glob if no match
var policy *PathPolicy
raw, ok := a.exactRules.Get(path)
if ok {
policy = raw.(*PathPolicy)
goto CHECK
}
// Check the rules for a match, default deny if no match
_, raw, ok = a.globRules.LongestPrefix(path)
if !ok {
return false
} else {
policy = raw.(*PathPolicy)
}
CHECK:
// Check the policy level
return policy.Policy == PathPolicySudo
}