-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathenforce.go
154 lines (125 loc) · 3.09 KB
/
enforce.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
152
153
154
package redtape
import (
"errors"
)
// Enforcer interface provides methods to enforce policies against a request.
type Enforcer interface {
Enforce(*Request) error
}
type enforcer struct {
manager PolicyManager
matcher Matcher
auditor Auditor
}
// NewEnforcer returns a default Enforcer combining a PolicyManager, Matcher, and Auditor.
func NewEnforcer(manager PolicyManager, matcher Matcher, auditor Auditor) (Enforcer, error) {
return &enforcer{
manager: manager,
matcher: matcher,
auditor: auditor,
}, nil
}
func NewDefaultEnforcer(manager PolicyManager) (Enforcer, error) {
return NewEnforcer(manager, DefaultMatcher, NewConsoleAuditor(AuditAll))
}
// Enforce fulfills the Enforce method of Enforcer. The default implementation matches the Request against
// the range of stored Policies and evaluating each.
// Polices are matched first by Action, then Role, Resource, Scope and finally Condition. If a match is found, the
// configured Policy Effect is applied.
// TODO: return explicit PolicyEffect and use error to indicate processing failures
func (e *enforcer) Enforce(r *Request) error {
allow := false
matched := []Policy{}
e.auditReq(r)
pol, err := e.manager.FindByRequest(r)
if err != nil {
return err
}
for _, p := range pol {
match, err := e.evalPolicy(r, p)
if err != nil {
return err
}
if !match {
continue
}
matched = append(matched, p)
// deny overrides all
if p.Effect() == PolicyEffectDeny {
e.auditEffect(r, PolicyEffectDeny)
return NewErrRequestDeniedExplicit(p)
}
allow = true
}
if !allow && DefaultPolicyEffect == PolicyEffectDeny {
e.auditEffect(r, PolicyEffectDeny)
return NewErrRequestDeniedImplicit(errors.New("access denied because no policy allowed access"))
}
e.auditEffect(r, PolicyEffectAllow)
return nil
}
func (e *enforcer) checkConditions(p Policy, r *Request) bool {
for key, cond := range p.Conditions() {
meta := RequestMetadataFromContext(r.Context)
if pass := cond.Meets(meta[key], r); !pass {
return false
}
}
return true
}
func (e *enforcer) evalPolicy(r *Request, p Policy) (bool, error) {
// match actions
am, err := e.matcher.MatchPolicy(p, p.Actions(), r.Action)
if err != nil {
return false, err
}
if !am {
return false, nil
}
rm := false
// match roles
for _, role := range p.Roles() {
b, err := e.matcher.MatchRole(role, r.Role)
if err != nil {
return false, err
}
if b {
rm = true
break
}
}
if !rm {
return false, nil
}
// match resources
resm, err := e.matcher.MatchPolicy(p, p.Resources(), r.Resource)
if err != nil {
return false, err
}
if !resm {
return false, nil
}
// match scopes
scm, err := e.matcher.MatchPolicy(p, p.Scopes(), r.Scope)
if err != nil {
return false, err
}
if !scm {
return false, nil
}
// check all conditions
if !e.checkConditions(p, r) {
return false, nil
}
return true, nil
}
func (e *enforcer) auditReq(req *Request) {
if e.auditor != nil {
e.auditor.LogRequest(req)
}
}
func (e *enforcer) auditEffect(req *Request, effect PolicyEffect) {
if e.auditor != nil {
e.auditor.LogPolicyEffect(req, effect)
}
}