-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathresolver.go
157 lines (127 loc) · 3.89 KB
/
resolver.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
155
156
157
package acl
import (
"context"
"fmt"
"sort"
log "github.com/edufschmidt/go-log"
radix "github.com/edufschmidt/go-radix-tree"
)
// SecretResolverFunc returns the token associated with a secret
// if it exists, or nil otherwise.
type SecretResolverFunc func(context.Context, string) (Token, error)
// PolicyResolverFunc returns a policy based on its name,
// or an error if it does not exist.
type PolicyResolverFunc func(context.Context, string) (Policy, error)
// Resolver resolves ACL secrets and policies.
type Resolver struct {
logger log.Logger
model *Model
resolveSecret SecretResolverFunc
resolvePolicy PolicyResolverFunc
}
// NewResolver ...
func NewResolver(config *ResolverConfig) (*Resolver, error) {
config = DefaultResolverConfig().Merge(config)
if config.Model == nil {
return nil, ErrMissingModel
}
if config.SecretResolver == nil {
return nil, ErrMissingSecretResolver
}
if config.PolicyResolver == nil {
return nil, ErrMissingPolicyResolver
}
return &Resolver{
logger: config.Logger,
model: config.Model,
resolveSecret: config.SecretResolver,
resolvePolicy: config.PolicyResolver,
}, nil
}
// SecretResolver configures how secrets are resolved to ACL tokens.
func (r *Resolver) SecretResolver(f SecretResolverFunc) {
r.resolveSecret = f
}
// PolicyResolver configures how policy names are resolved to ACL policies.
func (r *Resolver) PolicyResolver(f PolicyResolverFunc) {
r.resolvePolicy = f
}
// ResolveSecret creates an ACL from a secret.
func (r *Resolver) ResolveSecret(ctx context.Context, secret string) (*ACL, error) {
var token Token
// Handle anonymous requests.
tkn, err := r.resolveSecret(ctx, secret)
if err != nil {
return nil, fmt.Errorf("%v : %v", ErrResolvingSecret, err)
}
if tkn == nil {
return nil, ErrTokenNotFound
}
token = tkn
if token.IsPrivileged() {
return &ACL{privileged: true}, nil
}
// Retrieve policy definitions from the repository.
policies := []Policy{}
for _, p := range token.Policies() {
policy, err := r.resolvePolicy(ctx, p)
if err != nil {
return nil, fmt.Errorf("%v : %v", ErrResolvingPolicy, err)
}
policies = append(policies, policy)
}
sort.Slice(policies, func(i, j int) bool {
return policies[i].Name() < policies[j].Name()
})
// Initialize ACL struct
acl := &ACL{capabilities: map[string]*radix.Tree{}}
for resource := range r.model.resources {
acl.capabilities[resource] = radix.NewTree()
}
// Based on the token policies, we populate the
// capability trees for each resource type.
for _, policy := range policies {
for _, res := range r.model.resources {
rules := policy.Rules()
for _, rule := range rules {
var capabilities capMap
if rule.Resource() == res.name {
// Initialize capability map for this pattern if it has not yet been
// initialized by a previously processed rule/policy.
if leaf, found := acl.capabilities[res.name].Get(rule.Path()); !found {
capabilities = make(capMap)
acl.capabilities[res.name].Set(rule.Path(), capabilities)
} else {
capabilities = leaf.(capMap)
}
// Ignore all capabilities in the rule if a previously processed
// rule/policy has explicitly denied access to resources matching
// this pattern.
if capabilities.HasCapability(capabilityDeny) {
break
}
// Expand aliases before processing capabilities.
expanded := []string{}
for _, cap := range rule.Capabilities() {
if res.hasAlias(cap) {
expanded = append(expanded, res.aliases[cap].expand()...)
} else {
expanded = append(expanded, cap)
}
}
// Set all capabilities in the rule.
for _, cap := range expanded {
if cap == capabilityDeny {
capabilities.Clear()
capabilities.AddCapability(capabilityDeny)
break
} else {
capabilities.AddCapability(cap)
}
}
}
}
}
}
return acl, nil
}