blob: ba0413101ddb60a62969c435c3c9f28319cf2db1 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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 randum 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
31const (
32 letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
33 defaultSimpleTokenLength = 16
34)
35
36// var for testing purposes
37var (
38 simpleTokenTTLDefault = 300 * time.Second
39 simpleTokenTTLResolution = 1 * time.Second
40)
41
42type simpleTokenTTLKeeper struct {
43 tokens map[string]time.Time
44 donec chan struct{}
45 stopc chan struct{}
46 deleteTokenFunc func(string)
47 mu *sync.Mutex
48 simpleTokenTTL time.Duration
49}
50
51func (tm *simpleTokenTTLKeeper) stop() {
52 select {
53 case tm.stopc <- struct{}{}:
54 case <-tm.donec:
55 }
56 <-tm.donec
57}
58
59func (tm *simpleTokenTTLKeeper) addSimpleToken(token string) {
60 tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL)
61}
62
63func (tm *simpleTokenTTLKeeper) resetSimpleToken(token string) {
64 if _, ok := tm.tokens[token]; ok {
65 tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL)
66 }
67}
68
69func (tm *simpleTokenTTLKeeper) deleteSimpleToken(token string) {
70 delete(tm.tokens, token)
71}
72
73func (tm *simpleTokenTTLKeeper) run() {
74 tokenTicker := time.NewTicker(simpleTokenTTLResolution)
75 defer func() {
76 tokenTicker.Stop()
77 close(tm.donec)
78 }()
79 for {
80 select {
81 case <-tokenTicker.C:
82 nowtime := time.Now()
83 tm.mu.Lock()
84 for t, tokenendtime := range tm.tokens {
85 if nowtime.After(tokenendtime) {
86 tm.deleteTokenFunc(t)
87 delete(tm.tokens, t)
88 }
89 }
90 tm.mu.Unlock()
91 case <-tm.stopc:
92 return
93 }
94 }
95}
96
97type tokenSimple struct {
98 indexWaiter func(uint64) <-chan struct{}
99 simpleTokenKeeper *simpleTokenTTLKeeper
100 simpleTokensMu sync.Mutex
101 simpleTokens map[string]string // token -> username
102 simpleTokenTTL time.Duration
103}
104
105func (t *tokenSimple) genTokenPrefix() (string, error) {
106 ret := make([]byte, defaultSimpleTokenLength)
107
108 for i := 0; i < defaultSimpleTokenLength; i++ {
109 bInt, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
110 if err != nil {
111 return "", err
112 }
113
114 ret[i] = letters[bInt.Int64()]
115 }
116
117 return string(ret), nil
118}
119
120func (t *tokenSimple) assignSimpleTokenToUser(username, token string) {
121 t.simpleTokensMu.Lock()
122 defer t.simpleTokensMu.Unlock()
123 if t.simpleTokenKeeper == nil {
124 return
125 }
126
127 _, ok := t.simpleTokens[token]
128 if ok {
129 plog.Panicf("token %s is alredy used", token)
130 }
131
132 t.simpleTokens[token] = username
133 t.simpleTokenKeeper.addSimpleToken(token)
134}
135
136func (t *tokenSimple) invalidateUser(username string) {
137 if t.simpleTokenKeeper == nil {
138 return
139 }
140 t.simpleTokensMu.Lock()
141 for token, name := range t.simpleTokens {
142 if strings.Compare(name, username) == 0 {
143 delete(t.simpleTokens, token)
144 t.simpleTokenKeeper.deleteSimpleToken(token)
145 }
146 }
147 t.simpleTokensMu.Unlock()
148}
149
150func (t *tokenSimple) enable() {
151 if t.simpleTokenTTL <= 0 {
152 t.simpleTokenTTL = simpleTokenTTLDefault
153 }
154
155 delf := func(tk string) {
156 if username, ok := t.simpleTokens[tk]; ok {
157 plog.Infof("deleting token %s for user %s", tk, username)
158 delete(t.simpleTokens, tk)
159 }
160 }
161 t.simpleTokenKeeper = &simpleTokenTTLKeeper{
162 tokens: make(map[string]time.Time),
163 donec: make(chan struct{}),
164 stopc: make(chan struct{}),
165 deleteTokenFunc: delf,
166 mu: &t.simpleTokensMu,
167 simpleTokenTTL: t.simpleTokenTTL,
168 }
169 go t.simpleTokenKeeper.run()
170}
171
172func (t *tokenSimple) disable() {
173 t.simpleTokensMu.Lock()
174 tk := t.simpleTokenKeeper
175 t.simpleTokenKeeper = nil
176 t.simpleTokens = make(map[string]string) // invalidate all tokens
177 t.simpleTokensMu.Unlock()
178 if tk != nil {
179 tk.stop()
180 }
181}
182
183func (t *tokenSimple) info(ctx context.Context, token string, revision uint64) (*AuthInfo, bool) {
184 if !t.isValidSimpleToken(ctx, token) {
185 return nil, false
186 }
187 t.simpleTokensMu.Lock()
188 username, ok := t.simpleTokens[token]
189 if ok && t.simpleTokenKeeper != nil {
190 t.simpleTokenKeeper.resetSimpleToken(token)
191 }
192 t.simpleTokensMu.Unlock()
193 return &AuthInfo{Username: username, Revision: revision}, ok
194}
195
196func (t *tokenSimple) assign(ctx context.Context, username string, rev uint64) (string, error) {
197 // rev isn't used in simple token, it is only used in JWT
198 index := ctx.Value(AuthenticateParamIndex{}).(uint64)
199 simpleTokenPrefix := ctx.Value(AuthenticateParamSimpleTokenPrefix{}).(string)
200 token := fmt.Sprintf("%s.%d", simpleTokenPrefix, index)
201 t.assignSimpleTokenToUser(username, token)
202
203 return token, nil
204}
205
206func (t *tokenSimple) isValidSimpleToken(ctx context.Context, token string) bool {
207 splitted := strings.Split(token, ".")
208 if len(splitted) != 2 {
209 return false
210 }
211 index, err := strconv.Atoi(splitted[1])
212 if err != nil {
213 return false
214 }
215
216 select {
217 case <-t.indexWaiter(uint64(index)):
218 return true
219 case <-ctx.Done():
220 }
221
222 return false
223}
224
225func newTokenProviderSimple(indexWaiter func(uint64) <-chan struct{}, TokenTTL time.Duration) *tokenSimple {
226 return &tokenSimple{
227 simpleTokens: make(map[string]string),
228 indexWaiter: indexWaiter,
229 simpleTokenTTL: TokenTTL,
230 }
231}