github.com/Venafi/vcert/v5@v5.10.2/pkg/playbook/app/domain/connection.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 domain
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  
    24  	"github.com/Venafi/vcert/v5/pkg/endpoint"
    25  	"github.com/Venafi/vcert/v5/pkg/venafi"
    26  )
    27  
    28  // Connection represents the issuer that vCert will connect to
    29  // in order to issue certificates
    30  type Connection struct {
    31  	Credentials     Authentication  `yaml:"credentials,omitempty"`
    32  	Insecure        bool            `yaml:"insecure,omitempty"`
    33  	Platform        venafi.Platform `yaml:"platform,omitempty"`
    34  	TrustBundlePath string          `yaml:"trustBundle,omitempty"`
    35  	URL             string          `yaml:"url,omitempty"`
    36  }
    37  
    38  // GetConnectorType returns the type of vcert Connector this config will create
    39  func (c Connection) GetConnectorType() endpoint.ConnectorType {
    40  	switch c.Platform {
    41  	case venafi.Firefly:
    42  		return endpoint.ConnectorTypeFirefly
    43  	case venafi.TPP:
    44  		return endpoint.ConnectorTypeTPP
    45  	case venafi.TLSPCloud:
    46  		return endpoint.ConnectorTypeCloud
    47  	default:
    48  		return endpoint.ConnectorTypeFake
    49  	}
    50  }
    51  
    52  func (c Connection) validateTrustBundle() error {
    53  	_, err := os.Stat(c.TrustBundlePath)
    54  	if err != nil {
    55  		// TrustBundle does not exist in location
    56  		if errors.Is(err, os.ErrNotExist) {
    57  			return fmt.Errorf("%w: %s", ErrTrustBundleNotExist, c.TrustBundlePath)
    58  		}
    59  	}
    60  	return nil
    61  }
    62  
    63  // IsValid returns true if the Connection is supported by vcert
    64  // and has the necessary values to connect to the given platform
    65  func (c Connection) IsValid() (bool, error) {
    66  	switch c.Platform {
    67  	case venafi.TPP:
    68  		return isValidTpp(c)
    69  	case venafi.TLSPCloud:
    70  		return isValidVaaS(c)
    71  	case venafi.Firefly:
    72  		return isValidFirefly(c)
    73  	default:
    74  		return false, fmt.Errorf("invalid connection type %v", c.Platform)
    75  	}
    76  }
    77  
    78  func isValidTpp(c Connection) (bool, error) {
    79  	var rErr error = nil
    80  	rValid := true
    81  
    82  	// Credentials are not empty
    83  	if c.Credentials.AccessToken == "" && c.Credentials.RefreshToken == "" && c.Credentials.P12Task == "" {
    84  		rValid = false
    85  		rErr = errors.Join(rErr, ErrNoCredentials)
    86  	}
    87  
    88  	// TPP connector requires a url
    89  	if c.URL == "" {
    90  		rValid = false
    91  		rErr = errors.Join(rErr, ErrNoTPPURL)
    92  	}
    93  
    94  	// If specified, ensure TrustBundle exists
    95  	if c.TrustBundlePath != "" {
    96  		err := c.validateTrustBundle()
    97  		if err != nil {
    98  			rValid = false
    99  			rErr = errors.Join(rErr, err)
   100  		}
   101  	}
   102  
   103  	return rValid, rErr
   104  }
   105  
   106  func isValidVaaS(c Connection) (bool, error) {
   107  	// Check if an API key has been provided
   108  	apikey := false
   109  	if c.Credentials.APIKey != "" {
   110  		apikey = true
   111  	}
   112  
   113  	accesstoken := false
   114  	if c.Credentials.AccessToken != "" {
   115  		accesstoken = true
   116  	}
   117  
   118  	// Check if an TokenURL has been provided
   119  	tokenurl := false
   120  	if c.Credentials.TokenURL != "" {
   121  		tokenurl = true
   122  	}
   123  
   124  	// Check if externalJWT has been provided
   125  	externaljwt := false
   126  	if c.Credentials.ExternalJWT != "" {
   127  		externaljwt = true
   128  	}
   129  
   130  	// There's a valid service account IF both externalJWT and tokenURL provided
   131  	svcaccount := false
   132  	if externaljwt && tokenurl {
   133  		svcaccount = true
   134  	} else if externaljwt && !tokenurl {
   135  		// JWT Provided without token URL
   136  		return false, ErrNoVCPTokenURL
   137  	} else if tokenurl && !externaljwt {
   138  		// Token URL without an external JWT
   139  		return false, ErrNoExternalJWT
   140  	}
   141  
   142  	// At this point, there are no valid credentials. Figure out why.
   143  	if !apikey && !svcaccount && !accesstoken {
   144  		return false, ErrNoCredentials
   145  	}
   146  
   147  	// if we got here then at least one of the credential options was provided
   148  	if (svcaccount && apikey) || (svcaccount && accesstoken) || (apikey && accesstoken) {
   149  		// more than one credential option is not acceptable
   150  		return false, ErrAmbiguousVCPCreds
   151  	}
   152  
   153  	// if we got here then only one credential option was provided (which is what we want)
   154  	return true, nil
   155  }
   156  
   157  func isValidFirefly(c Connection) (bool, error) {
   158  
   159  	if c.URL == "" {
   160  		return false, ErrNoFireflyURL
   161  	}
   162  
   163  	// Auth method: User-Password
   164  	userPassword := false
   165  	if c.Credentials.User != "" && c.Credentials.Password != "" {
   166  		userPassword = true
   167  	}
   168  
   169  	//Auth method: Client Secret
   170  	cSecret := false
   171  	if c.Credentials.ClientSecret != "" {
   172  		cSecret = true
   173  	}
   174  
   175  	//Auth method: Access Token
   176  	token := false
   177  	if c.Credentials.AccessToken != "" {
   178  		token = true
   179  	}
   180  
   181  	if !userPassword && !cSecret && !token {
   182  		return false, ErrNoCredentials
   183  	}
   184  
   185  	// Auth method is AccessToken, no further validations required
   186  	if token {
   187  		return true, nil
   188  	}
   189  
   190  	//Validate ClientId
   191  	if c.Credentials.ClientId == "" {
   192  		return false, ErrNoClientId
   193  	}
   194  
   195  	// Validate Identity Provider values
   196  	if c.Credentials.IdentityProvider == nil || c.Credentials.IdentityProvider.TokenURL == "" {
   197  		return false, ErrNoIdentityProviderURL
   198  	}
   199  
   200  	return true, nil
   201  }