github.com/Venafi/vcert/v5@v5.10.2/pkg/playbook/app/service/tokenService.go (about)

     1  /*
     2   * Copyright 2023 Venafi, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *  http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package service
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"go.uber.org/zap"
    23  
    24  	"github.com/Venafi/vcert/v5/pkg/playbook/app/domain"
    25  	"github.com/Venafi/vcert/v5/pkg/playbook/app/parser"
    26  	"github.com/Venafi/vcert/v5/pkg/playbook/app/vcertutil"
    27  )
    28  
    29  // ValidateTPPCredentials checks that the TPP credentials are not expired.
    30  //
    31  // If expired, it will try to get a new token pair using the refreshToken.
    32  //
    33  // If the refreshing is successful it will save the new token pair in the playbook file.
    34  func ValidateTPPCredentials(playbook *domain.Playbook) error {
    35  	//Validate TPP tokens
    36  	if playbook.Config.Connection.Credentials.AccessToken != "" {
    37  		isValid, err := vcertutil.IsValidAccessToken(playbook.Config)
    38  		// Return any error besides 401 Unauthorized - need to properly handle errors unrelated to the state of the token (connectivity)
    39  		if err != nil && err.Error() != "failed to verify token. Message: 401 Unauthorized" {
    40  			return err
    41  		}
    42  		if isValid {
    43  			return nil
    44  		}
    45  	}
    46  
    47  	zap.L().Info("access token is invalid, missing, or expired")
    48  	if playbook.Config.Connection.Credentials.RefreshToken == "" && playbook.Config.Connection.Credentials.P12Task == "" {
    49  		return fmt.Errorf("access token no longer valid and no authorization methods specified - cannot get a new access token")
    50  	}
    51  
    52  	zap.L().Info("using refresh token")
    53  
    54  	// Read the playbook first, to make sure we can, before refreshing the tokens
    55  	// and blowing things up!
    56  	pbData, err := parser.ReadPlaybookRaw(playbook.Location)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	accessToken, refreshToken, err := vcertutil.RefreshTPPTokens(playbook.Config)
    62  	if err != nil {
    63  		zap.L().Error("failed to refresh TPP Tokens", zap.Error(err))
    64  		return err
    65  	}
    66  	zap.L().Info("successfully retrieved new refresh token")
    67  
    68  	playbook.Config.Connection.Credentials.AccessToken = accessToken
    69  	playbook.Config.Connection.Credentials.RefreshToken = refreshToken
    70  
    71  	err = replaceTokensInFile(pbData, accessToken, refreshToken)
    72  	if err != nil {
    73  		zap.L().Error("failed to replace tokens in playbook file", zap.Error(err))
    74  		return err
    75  	}
    76  
    77  	err = parser.WritePlaybook(pbData, playbook.Location)
    78  	if err != nil {
    79  		zap.L().Error("failed to serialize new tokens to playbook file", zap.Error(err))
    80  		return err
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func replaceTokensInFile(playbook map[string]interface{}, accessToken string, refreshToken string) error {
    87  
    88  	if playbook == nil {
    89  		return fmt.Errorf("playbook data is nil")
    90  	}
    91  	cfg, found := playbook["config"]
    92  	if !found {
    93  		return fmt.Errorf("no config found in Playbook data")
    94  	}
    95  
    96  	conn, found := cfg.(map[string]interface{})["connection"]
    97  	if !found {
    98  		return fmt.Errorf("no connection found in Playbook data")
    99  	}
   100  
   101  	creds, found := conn.(map[string]interface{})["credentials"]
   102  	if !found {
   103  		return fmt.Errorf("no credentials found in Playbook data")
   104  	}
   105  
   106  	credsMap := creds.(map[string]interface{})
   107  	credsMap["accessToken"] = accessToken
   108  	credsMap["refreshToken"] = refreshToken
   109  
   110  	return nil
   111  }