1 // pubsubplus-go-client 2 // 3 // Copyright 2021-2025 Solace Corporation. All rights reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package config 18 19 import ( 20 "time" 21 ) 22 23 // AuthenticationStrategy represents an authentication strategy. 24 type AuthenticationStrategy struct { 25 config ServicePropertyMap 26 } 27 28 func (strategy AuthenticationStrategy) String() string { 29 return strategy.config.String() 30 } 31 32 // ToProperties gets the configuration of the authentication strategy in the form of a ServicePropertyMap. 33 func (strategy AuthenticationStrategy) ToProperties() ServicePropertyMap { 34 // return empty map if config is nil 35 if strategy.config == nil { 36 return make(ServicePropertyMap) 37 } 38 // duplicate config 39 return strategy.config.GetConfiguration() 40 } 41 42 // BasicUserNamePasswordAuthentication gets a basic authentication strategy with the provided credentials. 43 func BasicUserNamePasswordAuthentication(username, password string) AuthenticationStrategy { 44 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 45 authenticationStrategy.config[AuthenticationPropertySchemeBasicUserName] = username 46 authenticationStrategy.config[AuthenticationPropertySchemeBasicPassword] = password 47 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeBasic 48 return authenticationStrategy 49 } 50 51 // ClientCertificateAuthentication gets a client certificate-based authentication strategy using 52 // the provided certificate file, key file, and key password if required. If no keyFile or keyPassword is 53 // required, use an empty string for the keyFile and keyPassword arguments. 54 func ClientCertificateAuthentication(certificateFile, keyFile, keyPassword string) AuthenticationStrategy { 55 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 56 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeClientCertificate 57 authenticationStrategy.config[AuthenticationPropertySchemeSSLClientCertFile] = certificateFile 58 if keyFile != "" { 59 authenticationStrategy.config[AuthenticationPropertySchemeSSLClientPrivateKeyFile] = keyFile 60 } 61 if keyPassword != "" { 62 authenticationStrategy.config[AuthenticationPropertySchemeClientCertPrivateKeyFilePassword] = keyPassword 63 } 64 return authenticationStrategy 65 } 66 67 // OAuth2Authentication creates an OAuth2-based authentication strategy using the specified tokens. 68 // At least one of accessToken or OIDC idToken must be provided. Optionally, issuerIdentifier 69 // can be provided. If any of the parameters is not required, an empty string can be passed. 70 func OAuth2Authentication(accessToken, oidcIDToken, issuerIdentifier string) AuthenticationStrategy { 71 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 72 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeOAuth2 73 if len(accessToken) > 0 { 74 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2AccessToken] = accessToken 75 } 76 // Only set the id token or issuer identifier if they are nonempty. 77 if len(oidcIDToken) > 0 { 78 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2OIDCIDToken] = oidcIDToken 79 } 80 if len(issuerIdentifier) > 0 { 81 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2IssuerIdentifier] = issuerIdentifier 82 } 83 return authenticationStrategy 84 } 85 86 // KerberosAuthentication creates a Kerberos-based authentication strategy. Optionally, 87 // a Kerberos service name can be provided. If the service name is not required, an empty 88 // string can be passed to the serviceName argument. 89 // 90 // To implement Kerberos authentication for clients connecting to a broker, the following 91 // configuration is required the broker: 92 // - A Kerberos Keytab must be loaded on the broker. See "Event Broker File Management" for more information 93 // on the Solace documentation website. 94 // - Kerberos authentication must be configured and enabled for any Message VPNs that 95 // Kerberos-authenticated clients will connect to. 96 // - Optional: On an appliance, a Kerberos Service Principal Name (SPN) can be assigned to the IP address 97 // for the message backbone VRF Kerberos‑authenticated clients will use. 98 // 99 // Further reference can be found at 100 // 101 // https://docs.solace.com/Configuring-and-Managing/Configuring-Client-Authentication.htm#Config-Kerberos 102 func KerberosAuthentication(serviceName string) AuthenticationStrategy { 103 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 104 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeKerberos 105 if len(serviceName) > 0 { 106 authenticationStrategy.config[AuthenticationPropertySchemeKerberosInstanceName] = serviceName 107 } 108 return authenticationStrategy 109 } 110 111 /* Retry Strategy */ 112 113 // RetryStrategy represents the strategy to use when reconnecting. 114 type RetryStrategy struct { 115 retries int 116 retryInterval time.Duration 117 } 118 119 // GetRetries returns the number of retries configured with the retry strategy. 120 func (strategy RetryStrategy) GetRetries() int { 121 return strategy.retries 122 } 123 124 // GetRetryInterval returns the interval between (re)connection retry attempts. 125 func (strategy RetryStrategy) GetRetryInterval() time.Duration { 126 return strategy.retryInterval 127 } 128 129 // RetryStrategyForeverRetry creates a retry strategy that runs forever 130 // with automatic retries and a retry interval of 3000 milliseconds. 131 func RetryStrategyForeverRetry() RetryStrategy { 132 return RetryStrategy{-1, 3000 * time.Millisecond} 133 } 134 135 // RetryStrategyForeverRetryWithInterval creates a retry strategy that runs forever 136 // with automatic retries and a retry interval of the specified duration. 137 func RetryStrategyForeverRetryWithInterval(retryInterval time.Duration) RetryStrategy { 138 return RetryStrategy{-1, retryInterval} 139 } 140 141 // RetryStrategyNeverRetry creates an instance of configuration for disabled automatic retries. 142 func RetryStrategyNeverRetry() RetryStrategy { 143 return RetryStrategy{0, 3000 * time.Millisecond} 144 } 145 146 // RetryStrategyParameterizedRetry creates an instance of configuration for free configurability. 147 // retries must be greater than or equal to zero. 148 func RetryStrategyParameterizedRetry(retries uint, retryInterval time.Duration) RetryStrategy { 149 return RetryStrategy{int(retries), retryInterval} 150 } 151 152 /* Transport Security Strategy */ 153 154 // TransportSecurityStrategy represents the strategy to use when connecting to a broker 155 // via a secure port. 156 type TransportSecurityStrategy struct { 157 config ServicePropertyMap 158 } 159 160 // TransportSecurityProtocol represents the various security protocols available to the API. 161 type TransportSecurityProtocol string 162 163 const ( 164 // TransportSecurityProtocolSSLv3 is deprecated, do not use. 165 TransportSecurityProtocolSSLv3 TransportSecurityProtocol = "SSLv3" 166 // TransportSecurityProtocolTLSv1 is deprecated, do not use. 167 TransportSecurityProtocolTLSv1 TransportSecurityProtocol = "TLSv1" 168 // TransportSecurityProtocolTLSv1_1 represents TLSv1.1. 169 TransportSecurityProtocolTLSv1_1 TransportSecurityProtocol = "TLSv1.1" 170 // TransportSecurityProtocolTLSv1_2 represents TLSv1.2. 171 TransportSecurityProtocolTLSv1_2 TransportSecurityProtocol = "TLSv1.2" 172 // TransportSecurityProtocolTLSv1_3 represents TLSv1.3. 173 TransportSecurityProtocolTLSv1_3 TransportSecurityProtocol = "TLSv1.3" 174 ) 175 176 // NewTransportSecurityStrategy creates a transport security strategy with default configuration. 177 // Properties can be overwritten by calling the various configuration functions on TransportSecurityStrategy. 178 func NewTransportSecurityStrategy() TransportSecurityStrategy { 179 tss := TransportSecurityStrategy{ 180 config: make(ServicePropertyMap), 181 } 182 return tss 183 } 184 185 // Downgradable configures TLS so that the session connection is downgraded to plain-text after client authentication. 186 // WARNING: Downgrading SSL to plain-text after client authentication exposes a client and the data being sent 187 // to higher security risks. 188 func (tss TransportSecurityStrategy) Downgradable() TransportSecurityStrategy { 189 tss.config[TransportLayerSecurityPropertyProtocolDowngradeTo] = DowngradeToPlaintext 190 return tss 191 } 192 193 // WithExcludedProtocols is deprecated, use WithMinimumProtocol and WithMaximumProtocol instead. 194 func (tss TransportSecurityStrategy) WithExcludedProtocols(protocols ...TransportSecurityProtocol) TransportSecurityStrategy { 195 protocolsCS := "" 196 for _, protocol := range protocols { 197 if protocolsCS != "" { 198 protocolsCS += "," 199 } 200 protocolsCS += string(protocol) 201 } 202 tss.config[TransportLayerSecurityPropertyExcludedProtocols] = protocolsCS 203 return tss 204 } 205 206 // WithMinimumProtocol specifies the lowest TLS protocol version to allow. 207 func (tss TransportSecurityStrategy) WithMinimumProtocol(protocol TransportSecurityProtocol) TransportSecurityStrategy { 208 tss.config[TransportLayerSecurityPropertyMinimumProtocol] = string(protocol) 209 return tss 210 } 211 212 // WithMaximumProtocol specifies the highest TLS protocol version to negotiate. 213 func (tss TransportSecurityStrategy) WithMaximumProtocol(protocol TransportSecurityProtocol) TransportSecurityStrategy { 214 tss.config[TransportLayerSecurityPropertyMaximumProtocol] = string(protocol) 215 return tss 216 } 217 218 // WithoutCertificateValidation configures TLS to not validate the server certificate 219 // configured on the remote broker. 220 // WARNING: Disabling certificate validation exposes clients and data being sent to higher security risks. 221 func (tss TransportSecurityStrategy) WithoutCertificateValidation() TransportSecurityStrategy { 222 tss.config[TransportLayerSecurityPropertyCertValidated] = false 223 return tss 224 } 225 226 // WithCertificateValidation configures TLS validation on certificates. By default, validation is performed. 227 // WARNING: Disabling certificate validation exposes clients and data being sent to higher security risks. 228 // 229 // ignoreExpiration: when set to true, expired certificates are accepted. 230 // 231 // validateServerName: When set to true, certificates without the matching host are not accepted. 232 // 233 // trustStoreFilePath: The location of the trust store files. If an empty string is passed, no file path will be set. 234 // 235 // trustedCommonNameList: A comma-separated list of acceptable common names for matching with server certificates. An empty string will match no names. 236 func (tss TransportSecurityStrategy) WithCertificateValidation( 237 ignoreExpiration bool, 238 validateServerName bool, 239 trustStoreFilePath string, 240 trustedCommonNameList string, 241 ) TransportSecurityStrategy { 242 tss.config[TransportLayerSecurityPropertyCertValidated] = true 243 tss.config[TransportLayerSecurityPropertyCertRejectExpired] = !ignoreExpiration 244 tss.config[TransportLayerSecurityPropertyCertValidateServername] = validateServerName 245 if trustStoreFilePath != "" { 246 tss.config[TransportLayerSecurityPropertyTrustStorePath] = trustStoreFilePath 247 } 248 if trustedCommonNameList != "" { 249 tss.config[TransportLayerSecurityPropertyTrustedCommonNameList] = trustedCommonNameList 250 } 251 return tss 252 } 253 254 // WithCipherSuites configures cipher suites to use with TLSv1.2 and older. Has no effect when TLSv1.3 is negotiated. 255 // The cipher suites value is a comma-separated list of cipher suites and must be from the following table: 256 // 257 // +-----------------+-------------------------------+--------------------+ 258 // | 'AES256-SHA' | 'ECDHE-RSA-AES256-SHA' | 'AES256-GCM-SHA384'| 259 // +-----------------+-------------------------------+--------------------+ 260 // | 'AES256-SHA256' | 'ECDHE-RSA-AES256-GCM-SHA384' | 'AES128-SHA256' | 261 // +-----------------+-------------------------------+--------------------+ 262 // | 'DES-CBC3-SHA' | 'ECDHE-RSA-DES-CBC3-SHA' | | 263 // +-----------------+-------------------------------+--------------------+ 264 // | 'RC4-SHA' | 'ECDHE-RSA-AES256-SHA384' | 'AES128 | 265 // +-----------------+-------------------------------+--------------------+ 266 // | 'ECDHE-RSA-AES128-SHA256' | 'AES128-GCM-SHA256'| 267 // +-----------------+-------------------------------+--------------------+ 268 // | 'RC4-MD5' | 'ECDHE-RSA-AES128-GCM-SHA256' | | 269 // +----------------------------------------+-----------------------------+ 270 // | 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'| 'ECDHE-RSA-AES128-SHA' | 271 // +----------------------------------------+-----------------------------+ 272 // | 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'| | 273 // +----------------------------------------+-----------------------------+ 274 // | 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' | | 275 // +----------------------------------------+-----------------------------+ 276 // | 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA' | | 277 // +----------------------------------------+-----------------------------+ 278 // | 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'| | 279 // +----------------------------------------+-----------------------------+ 280 // | 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' | | 281 // +----------------------------------------+-----------------------------+ 282 // | 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'| | 283 // +-----------------------------------+----------------------------------+ 284 // | 'TLS_RSA_WITH_AES_128_GCM_SHA256' | | 285 // +-----------------------------------+----------------------------------+ 286 // | 'TLS_RSA_WITH_AES_128_CBC_SHA256' |'TLS_RSA_WITH_AES_256_GCM_SHA384' | 287 // +-----------------------------------+----------------------------------+ 288 // | 'TLS_RSA_WITH_AES_256_CBC_SHA256' | 'TLS_RSA_WITH_AES_256_CBC_SHA' | 289 // +-----------------------------------+----------------------------------+ 290 // | 'SSL_RSA_WITH_3DES_EDE_CBC_SHA | 'TLS_RSA_WITH_AES_128_CBC_SHA' | 291 // +-----------------------------------+----------------------------------+ 292 // | 'SSL_RSA_WITH_RC4_128_SHA' | 'SSL_RSA_WITH_RC4_128_MD5' | 293 // +-----------------------------------+----------------------------------+ 294 func (tss TransportSecurityStrategy) WithCipherSuites(cipherSuiteList string) TransportSecurityStrategy { 295 tss.config[TransportLayerSecurityPropertyCipherSuites] = cipherSuiteList 296 return tss 297 } 298 299 // ToProperties returns the configuration in the form of a ServicePropertyMap. 300 func (tss TransportSecurityStrategy) ToProperties() ServicePropertyMap { 301 if tss.config == nil { 302 return make(ServicePropertyMap) 303 } 304 return tss.config.GetConfiguration() 305 } 306