go.etcd.io/etcd@v3.3.27+incompatible/auth/simple_token.go (about) 1 // 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 15 package 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 20 import ( 21 "context" 22 "crypto/rand" 23 "fmt" 24 "math/big" 25 "strconv" 26 "strings" 27 "sync" 28 "time" 29 ) 30 31 const ( 32 letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 33 defaultSimpleTokenLength = 16 34 ) 35 36 // var for testing purposes 37 var ( 38 simpleTokenTTLDefault = 300 * time.Second 39 simpleTokenTTLResolution = 1 * time.Second 40 ) 41 42 type 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 51 func (tm *simpleTokenTTLKeeper) stop() { 52 select { 53 case tm.stopc <- struct{}{}: 54 case <-tm.donec: 55 } 56 <-tm.donec 57 } 58 59 func (tm *simpleTokenTTLKeeper) addSimpleToken(token string) { 60 tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL) 61 } 62 63 func (tm *simpleTokenTTLKeeper) resetSimpleToken(token string) { 64 if _, ok := tm.tokens[token]; ok { 65 tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL) 66 } 67 } 68 69 func (tm *simpleTokenTTLKeeper) deleteSimpleToken(token string) { 70 delete(tm.tokens, token) 71 } 72 73 func (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 97 type 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 105 func (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 120 func (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 136 func (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 150 func (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 172 func (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 183 func (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 196 func (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 206 func (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 225 func 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 }