blob: 934978c9857ea352243fae68d59f6922a0768bf1 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package auth
16
17// CAUTION: This random number based token mechanism is only for testing purpose.
18// JWT based mechanism will be added in the near future.
19
20import (
21 "context"
22 "crypto/rand"
23 "fmt"
24 "math/big"
25 "strconv"
26 "strings"
27 "sync"
28 "time"
29
30 "go.uber.org/zap"
31)
32
33const (
34 letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
35 defaultSimpleTokenLength = 16
36)
37
38// var for testing purposes
39var (
40 simpleTokenTTL = 5 * time.Minute
41 simpleTokenTTLResolution = 1 * time.Second
42)
43
44type simpleTokenTTLKeeper struct {
45 tokens map[string]time.Time
46 donec chan struct{}
47 stopc chan struct{}
48 deleteTokenFunc func(string)
49 mu *sync.Mutex
50}
51
52func (tm *simpleTokenTTLKeeper) stop() {
53 select {
54 case tm.stopc <- struct{}{}:
55 case <-tm.donec:
56 }
57 <-tm.donec
58}
59
60func (tm *simpleTokenTTLKeeper) addSimpleToken(token string) {
61 tm.tokens[token] = time.Now().Add(simpleTokenTTL)
62}
63
64func (tm *simpleTokenTTLKeeper) resetSimpleToken(token string) {
65 if _, ok := tm.tokens[token]; ok {
66 tm.tokens[token] = time.Now().Add(simpleTokenTTL)
67 }
68}
69
70func (tm *simpleTokenTTLKeeper) deleteSimpleToken(token string) {
71 delete(tm.tokens, token)
72}
73
74func (tm *simpleTokenTTLKeeper) run() {
75 tokenTicker := time.NewTicker(simpleTokenTTLResolution)
76 defer func() {
77 tokenTicker.Stop()
78 close(tm.donec)
79 }()
80 for {
81 select {
82 case <-tokenTicker.C:
83 nowtime := time.Now()
84 tm.mu.Lock()
85 for t, tokenendtime := range tm.tokens {
86 if nowtime.After(tokenendtime) {
87 tm.deleteTokenFunc(t)
88 delete(tm.tokens, t)
89 }
90 }
91 tm.mu.Unlock()
92 case <-tm.stopc:
93 return
94 }
95 }
96}
97
98type tokenSimple struct {
99 lg *zap.Logger
100 indexWaiter func(uint64) <-chan struct{}
101 simpleTokenKeeper *simpleTokenTTLKeeper
102 simpleTokensMu sync.Mutex
103 simpleTokens map[string]string // token -> username
104}
105
106func (t *tokenSimple) genTokenPrefix() (string, error) {
107 ret := make([]byte, defaultSimpleTokenLength)
108
109 for i := 0; i < defaultSimpleTokenLength; i++ {
110 bInt, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
111 if err != nil {
112 return "", err
113 }
114
115 ret[i] = letters[bInt.Int64()]
116 }
117
118 return string(ret), nil
119}
120
121func (t *tokenSimple) assignSimpleTokenToUser(username, token string) {
122 t.simpleTokensMu.Lock()
123 defer t.simpleTokensMu.Unlock()
124 if t.simpleTokenKeeper == nil {
125 return
126 }
127
128 _, ok := t.simpleTokens[token]
129 if ok {
130 if t.lg != nil {
131 t.lg.Panic(
132 "failed to assign already-used simple token to a user",
133 zap.String("user-name", username),
134 zap.String("token", token),
135 )
136 } else {
137 plog.Panicf("token %s is already used", token)
138 }
139 }
140
141 t.simpleTokens[token] = username
142 t.simpleTokenKeeper.addSimpleToken(token)
143}
144
145func (t *tokenSimple) invalidateUser(username string) {
146 if t.simpleTokenKeeper == nil {
147 return
148 }
149 t.simpleTokensMu.Lock()
150 for token, name := range t.simpleTokens {
151 if name == username {
152 delete(t.simpleTokens, token)
153 t.simpleTokenKeeper.deleteSimpleToken(token)
154 }
155 }
156 t.simpleTokensMu.Unlock()
157}
158
159func (t *tokenSimple) enable() {
160 delf := func(tk string) {
161 if username, ok := t.simpleTokens[tk]; ok {
162 if t.lg != nil {
163 t.lg.Info(
164 "deleted a simple token",
165 zap.String("user-name", username),
166 zap.String("token", tk),
167 )
168 } else {
169 plog.Infof("deleting token %s for user %s", tk, username)
170 }
171 delete(t.simpleTokens, tk)
172 }
173 }
174 t.simpleTokenKeeper = &simpleTokenTTLKeeper{
175 tokens: make(map[string]time.Time),
176 donec: make(chan struct{}),
177 stopc: make(chan struct{}),
178 deleteTokenFunc: delf,
179 mu: &t.simpleTokensMu,
180 }
181 go t.simpleTokenKeeper.run()
182}
183
184func (t *tokenSimple) disable() {
185 t.simpleTokensMu.Lock()
186 tk := t.simpleTokenKeeper
187 t.simpleTokenKeeper = nil
188 t.simpleTokens = make(map[string]string) // invalidate all tokens
189 t.simpleTokensMu.Unlock()
190 if tk != nil {
191 tk.stop()
192 }
193}
194
195func (t *tokenSimple) info(ctx context.Context, token string, revision uint64) (*AuthInfo, bool) {
196 if !t.isValidSimpleToken(ctx, token) {
197 return nil, false
198 }
199 t.simpleTokensMu.Lock()
200 username, ok := t.simpleTokens[token]
201 if ok && t.simpleTokenKeeper != nil {
202 t.simpleTokenKeeper.resetSimpleToken(token)
203 }
204 t.simpleTokensMu.Unlock()
205 return &AuthInfo{Username: username, Revision: revision}, ok
206}
207
208func (t *tokenSimple) assign(ctx context.Context, username string, rev uint64) (string, error) {
209 // rev isn't used in simple token, it is only used in JWT
210 index := ctx.Value(AuthenticateParamIndex{}).(uint64)
211 simpleTokenPrefix := ctx.Value(AuthenticateParamSimpleTokenPrefix{}).(string)
212 token := fmt.Sprintf("%s.%d", simpleTokenPrefix, index)
213 t.assignSimpleTokenToUser(username, token)
214
215 return token, nil
216}
217
218func (t *tokenSimple) isValidSimpleToken(ctx context.Context, token string) bool {
219 splitted := strings.Split(token, ".")
220 if len(splitted) != 2 {
221 return false
222 }
223 index, err := strconv.Atoi(splitted[1])
224 if err != nil {
225 return false
226 }
227
228 select {
229 case <-t.indexWaiter(uint64(index)):
230 return true
231 case <-ctx.Done():
232 }
233
234 return false
235}
236
237func newTokenProviderSimple(lg *zap.Logger, indexWaiter func(uint64) <-chan struct{}) *tokenSimple {
238 return &tokenSimple{
239 lg: lg,
240 simpleTokens: make(map[string]string),
241 indexWaiter: indexWaiter,
242 }
243}