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  }