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 }