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