/
authmethods.go
203 lines (168 loc) · 5.4 KB
/
authmethods.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package authmethod
import (
"context"
"fmt"
"sort"
"sync"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/go-hclog"
"github.com/mitchellh/mapstructure"
)
type Cache interface {
// GetValidator retrieves the Validator from the cache.
// It returns the modify index of struct that the validator was created from,
// the validator and a boolean indicating whether the value was found
GetValidator(method *structs.ACLAuthMethod) (uint64, Validator, bool)
// PutValidatorIfNewer inserts a new validator into the cache if the index is greater
// than the modify index of any existing entry in the cache. This method will return
// the newest validator which may or may not be the one from the method parameter
PutValidatorIfNewer(method *structs.ACLAuthMethod, validator Validator, idx uint64) Validator
// Purge removes all cached validators
Purge()
}
type ValidatorFactory func(logger hclog.Logger, method *structs.ACLAuthMethod) (Validator, error)
type Validator interface {
// Name returns the name of the auth method backing this validator.
Name() string
// NewIdentity creates a blank identity populated with empty values.
NewIdentity() *Identity
// ValidateLogin takes raw user-provided auth method metadata and ensures
// it is sane, provably correct, and currently valid. Relevant identifying
// data is extracted and returned for immediate use by the role binding
// process.
//
// Depending upon the method, it may make sense to use these calls to
// continue to extend the life of the underlying token.
//
// Returns auth method specific metadata suitable for the Role Binding
// process as well as the desired enterprise meta for the token to be
// created.
ValidateLogin(ctx context.Context, loginToken string) (*Identity, error)
// Stop should be called to cease any background activity and free up
// resources.
Stop()
}
type Identity struct {
// SelectableFields is the format of this Identity suitable for selection
// with a binding rule.
SelectableFields interface{}
// ProjectedVars is the format of this Identity suitable for interpolation
// in a bind name within a binding rule.
ProjectedVars map[string]string
*structs.EnterpriseMeta
}
// ProjectedVarNames returns just the keyspace of the ProjectedVars map.
func (i *Identity) ProjectedVarNames() []string {
v := make([]string, 0, len(i.ProjectedVars))
for k := range i.ProjectedVars {
v = append(v, k)
}
return v
}
var (
typesMu sync.RWMutex
types = make(map[string]ValidatorFactory)
)
// Register makes an auth method with the given type available for use. If
// Register is called twice with the same name or if validator is nil, it
// panics.
func Register(name string, factory ValidatorFactory) {
typesMu.Lock()
defer typesMu.Unlock()
if factory == nil {
panic("authmethod: Register factory is nil for type " + name)
}
if _, dup := types[name]; dup {
panic("authmethod: Register called twice for type " + name)
}
types[name] = factory
}
func IsRegisteredType(typeName string) bool {
typesMu.RLock()
_, ok := types[typeName]
typesMu.RUnlock()
return ok
}
type authMethodValidatorEntry struct {
Validator Validator
ModifyIndex uint64
}
// authMethodCache is an non-thread-safe cache that maps ACLAuthMethods to their Validators
type authMethodCache struct {
entries map[string]*authMethodValidatorEntry
}
func newCache() Cache {
c := &authMethodCache{}
c.init()
return c
}
func (c *authMethodCache) init() {
c.Purge()
}
func (c *authMethodCache) GetValidator(method *structs.ACLAuthMethod) (uint64, Validator, bool) {
entry, ok := c.entries[method.Name]
if ok {
return entry.ModifyIndex, entry.Validator, true
}
return 0, nil, false
}
func (c *authMethodCache) PutValidatorIfNewer(method *structs.ACLAuthMethod, validator Validator, idx uint64) Validator {
prev, ok := c.entries[method.Name]
if ok {
if prev.ModifyIndex >= idx {
return prev.Validator
}
prev.Validator.Stop()
}
c.entries[method.Name] = &authMethodValidatorEntry{
Validator: validator,
ModifyIndex: idx,
}
return validator
}
func (c *authMethodCache) Purge() {
for _, entry := range c.entries {
entry.Validator.Stop()
}
c.entries = make(map[string]*authMethodValidatorEntry)
}
// NewValidator instantiates a new Validator for the given auth method
// configuration. If no auth method is registered with the provided type an
// error is returned.
func NewValidator(logger hclog.Logger, method *structs.ACLAuthMethod) (Validator, error) {
typesMu.RLock()
factory, ok := types[method.Type]
typesMu.RUnlock()
if !ok {
return nil, fmt.Errorf("no auth method registered with type: %s", method.Type)
}
logger = logger.Named("authmethod").With("type", method.Type, "name", method.Name)
return factory(logger, method)
}
// Types returns a sorted list of the names of the registered types.
func Types() []string {
typesMu.RLock()
defer typesMu.RUnlock()
var list []string
for name := range types {
list = append(list, name)
}
sort.Strings(list)
return list
}
// ParseConfig parses the config block for a auth method.
func ParseConfig(rawConfig map[string]interface{}, out interface{}) error {
decodeConf := &mapstructure.DecoderConfig{
Result: out,
WeaklyTypedInput: true,
ErrorUnused: true,
}
decoder, err := mapstructure.NewDecoder(decodeConf)
if err != nil {
return err
}
if err := decoder.Decode(rawConfig); err != nil {
return fmt.Errorf("error decoding config: %s", err)
}
return nil
}