github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/auth/authorize/token_provider.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package authorize
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/golang-jwt/jwt"
    28  	"github.com/pkg/errors"
    29  
    30  	"github.com/1aal/kubeblocks/pkg/cli/cmd/auth/authorize/authenticator"
    31  )
    32  
    33  type TokenProvider struct {
    34  	cached CachedTokenProvider
    35  	issued IssuedTokenProvider
    36  }
    37  
    38  // NewTokenProvider default constructor.
    39  func NewTokenProvider(o Options) (Provider, error) {
    40  	cached := NewKeyringCachedTokenProvider(nil)
    41  	issued, err := newDefaultIssuedTokenProvider(o)
    42  	if err != nil {
    43  		return nil, errors.Wrap(err, "could not create cloud issued token provider")
    44  	}
    45  	return &TokenProvider{
    46  		cached: cached,
    47  		issued: issued,
    48  	}, nil
    49  }
    50  
    51  // Abstract constructor
    52  func newTokenProvider(cached CachedTokenProvider, issued IssuedTokenProvider) Provider {
    53  	return &TokenProvider{
    54  		cached: cached,
    55  		issued: issued,
    56  	}
    57  }
    58  
    59  func (p *TokenProvider) Login(ctx context.Context) (*authenticator.UserInfoResponse, string, error) {
    60  	tokenResult, err := p.issued.authenticate(ctx)
    61  	if err != nil {
    62  		return nil, "", errors.Wrap(err, "could not authenticate with cloud")
    63  	}
    64  	userInfo, err := p.issued.getUserInfo(tokenResult.IDToken)
    65  	if err != nil {
    66  		return nil, "", errors.Wrap(err, "could not get user info from cloud")
    67  	}
    68  	err = p.cached.cacheUserInfo(userInfo)
    69  	if err != nil {
    70  		return nil, "", errors.Wrap(err, "could not store user info")
    71  	}
    72  
    73  	err = p.cached.cacheTokens(tokenResult)
    74  	if err != nil {
    75  		return nil, "", errors.Wrap(err, "could not cache tokens")
    76  	}
    77  
    78  	return userInfo, tokenResult.IDToken, nil
    79  }
    80  
    81  func (p *TokenProvider) Logout(ctx context.Context) error {
    82  	tokenResult, err := p.cached.GetTokens()
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if tokenResult == nil {
    87  		return fmt.Errorf("token not found in cache, already logged out")
    88  	}
    89  
    90  	err = p.cached.deleteTokens()
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	err = p.issued.logout(ctx, tokenResult.IDToken)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	return nil
   100  }
   101  
   102  func (p *TokenProvider) getTokenFromCache(isTokenValid func(authenticator.TokenResponse) bool) (*authenticator.TokenResponse, error) {
   103  	tokenResult, err := p.cached.GetTokens()
   104  	if err != nil {
   105  		return nil, errors.Wrap(err, "could get tokens from the cache")
   106  	}
   107  	// if the token is not in the cache, return nil
   108  	if tokenResult == nil {
   109  		return nil, nil
   110  	}
   111  
   112  	if isTokenValid(*tokenResult) {
   113  		return tokenResult, nil
   114  	}
   115  
   116  	if tokenResult.RefreshToken == "" {
   117  		return nil, nil
   118  	}
   119  
   120  	return p.getRefreshToken(tokenResult.RefreshToken), nil
   121  }
   122  
   123  // getRefreshToken gets a new token from the refresh token
   124  func (p *TokenProvider) getRefreshToken(refreshToken string) *authenticator.TokenResponse {
   125  	tokenResult, err := p.issued.refreshToken(refreshToken)
   126  	if err != nil {
   127  		return nil
   128  	}
   129  	return tokenResult
   130  }
   131  
   132  // IsValidToken checks to see if the token is valid and has not expired
   133  func IsValidToken(tokenString string) bool {
   134  	jwtParser := jwt.Parser{}
   135  	claims := jwt.MapClaims{}
   136  	if _, _, err := jwtParser.ParseUnverified(tokenString, claims); err != nil {
   137  		fmt.Println("Token parsing failed:", err)
   138  		return false
   139  	}
   140  	return claims.VerifyExpiresAt(time.Now().Unix(), true)
   141  }