This is an automated email from the ASF dual-hosted git repository.
curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new c94cd3816 feat(csharp/src/Drivers/Apache): Implement Standard SSL mode
for Impala (#2745)
c94cd3816 is described below
commit c94cd3816444b854b452031b0ef423e90ad141c3
Author: Sudhir Reddy Emmadi <[email protected]>
AuthorDate: Fri May 2 22:25:49 2025 +0530
feat(csharp/src/Drivers/Apache): Implement Standard SSL mode for Impala
(#2745)
Co-authored-by: Sudhir Emmadi <[email protected]>
---
.../Drivers/Apache/Hive2/HiveServer2Parameters.cs | 9 ++++
.../src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs | 61 ++++++++++++++++++++--
.../Apache/Impala/ImpalaStandardConnection.cs | 23 ++++++--
csharp/src/Drivers/Apache/Impala/README.md | 9 +++-
.../Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs | 42 +++++++++++++--
5 files changed, 132 insertions(+), 12 deletions(-)
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
b/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
index f1bb90f11..5d44948bf 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2Parameters.cs
@@ -54,4 +54,13 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
public const string TrustedCertificatePath =
"adbc.http_options.tls.trusted_certificate_path";
public const string DisableServerCertificateValidation =
"adbc.http_options.tls.disable_server_certificate_validation";
}
+
+ public static class StandardTlsOptions
+ {
+ public const string IsTlsEnabled = "adbc.standard_options.tls.enabled";
+ public const string AllowSelfSigned =
"adbc.standard_options.tls.allow_self_signed";
+ public const string AllowHostnameMismatch =
"adbc.standard_options.tls.allow_hostname_mismatch";
+ public const string TrustedCertificatePath =
"adbc.standard_options.tls.trusted_certificate_path";
+ public const string DisableServerCertificateValidation =
"adbc.standard_options.tls.disable_server_certificate_validation";
+ }
}
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs
b/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs
index 7c3d51c3f..b856d38d7 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs
@@ -37,15 +37,19 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
{
static internal TlsProperties
GetHttpTlsOptions(IReadOnlyDictionary<string, string> properties)
{
- TlsProperties tlsProperties = new TlsProperties();
+ TlsProperties tlsProperties = new();
if (properties.TryGetValue(AdbcOptions.Uri, out string? uri) &&
!string.IsNullOrWhiteSpace(uri))
{
var uriValue = new Uri(uri);
- tlsProperties.IsTlsEnabled = uriValue.Scheme ==
Uri.UriSchemeHttps || (properties.TryGetValue(HttpTlsOptions.IsTlsEnabled, out
string? isSslEnabled) && bool.TryParse(isSslEnabled, out bool isSslEnabledBool)
&& isSslEnabledBool);
+ tlsProperties.IsTlsEnabled = uriValue.Scheme ==
Uri.UriSchemeHttps || !properties.TryGetValue(HttpTlsOptions.IsTlsEnabled, out
string? isTlsEnabled) || !bool.TryParse(isTlsEnabled, out bool
isTlsEnabledBool) || isTlsEnabledBool;
}
- else if (properties.TryGetValue(HttpTlsOptions.IsTlsEnabled, out
string? isSslEnabled) && bool.TryParse(isSslEnabled, out bool isSslEnabledBool))
+ else if (!properties.TryGetValue(HttpTlsOptions.IsTlsEnabled, out
string? isTlsEnabled) || !bool.TryParse(isTlsEnabled, out bool
isTlsEnabledBool))
{
- tlsProperties.IsTlsEnabled = isSslEnabledBool;
+ tlsProperties.IsTlsEnabled = true;
+ }
+ else
+ {
+ tlsProperties.IsTlsEnabled = isTlsEnabledBool;
}
if (!tlsProperties.IsTlsEnabled)
{
@@ -97,5 +101,54 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
}
return httpClientHandler;
}
+
+ static internal TlsProperties
GetStandardTlsOptions(IReadOnlyDictionary<string, string> properties)
+ {
+ TlsProperties tlsProperties = new();
+ // tls is enabled by default
+ if (!properties.TryGetValue(StandardTlsOptions.IsTlsEnabled, out
string? isTlsEnabled) || !bool.TryParse(isTlsEnabled, out bool
isTlsEnabledBool))
+ {
+ tlsProperties.IsTlsEnabled = true;
+ }
+ else
+ {
+ tlsProperties.IsTlsEnabled = isTlsEnabledBool;
+ }
+ if (!tlsProperties.IsTlsEnabled)
+ {
+ return tlsProperties;
+ }
+
+ if
(properties.TryGetValue(StandardTlsOptions.DisableServerCertificateValidation,
out string? disableServerCertificateValidation) &&
bool.TryParse(disableServerCertificateValidation, out bool
disableServerCertificateValidationBool) &&
disableServerCertificateValidationBool)
+ {
+ tlsProperties.DisableServerCertificateValidation = true;
+ return tlsProperties;
+ }
+ tlsProperties.DisableServerCertificateValidation = false;
+ tlsProperties.AllowHostnameMismatch =
properties.TryGetValue(StandardTlsOptions.AllowHostnameMismatch, out string?
allowHostnameMismatch) && bool.TryParse(allowHostnameMismatch, out bool
allowHostnameMismatchBool) && allowHostnameMismatchBool;
+ tlsProperties.AllowSelfSigned =
properties.TryGetValue(StandardTlsOptions.AllowSelfSigned, out string?
allowSelfSigned) && bool.TryParse(allowSelfSigned, out bool
allowSelfSignedBool) && allowSelfSignedBool;
+ if (tlsProperties.AllowSelfSigned)
+ {
+ if
(!properties.TryGetValue(StandardTlsOptions.TrustedCertificatePath, out string?
trustedCertificatePath)) return tlsProperties;
+ tlsProperties.TrustedCertificatePath = trustedCertificatePath
!= "" && File.Exists(trustedCertificatePath) ? trustedCertificatePath : throw
new FileNotFoundException("Trusted certificate path is invalid or file does not
exist.");
+ }
+ return tlsProperties;
+ }
+
+ static internal RemoteCertificateValidationCallback
GetCertificateValidator(TlsProperties tlsProperties)
+ {
+ return (object sender, X509Certificate? certificate, X509Chain?
chain, SslPolicyErrors policyErrors) =>
+ {
+ if (policyErrors == SslPolicyErrors.None ||
tlsProperties.DisableServerCertificateValidation) return true;
+ if (string.IsNullOrEmpty(tlsProperties.TrustedCertificatePath))
+ {
+ return
+
(!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) ||
tlsProperties.AllowSelfSigned)
+ &&
(!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) ||
tlsProperties.AllowHostnameMismatch);
+ }
+
+ return false;
+ };
+ }
}
}
diff --git a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
index f5c84e2cd..c1cc1320e 100644
--- a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
+++ b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs
@@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Net;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Apache.Arrow.Adbc.Drivers.Apache.Hive2;
@@ -95,7 +96,7 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Impala
{
Properties.TryGetValue(ImpalaParameters.DataTypeConv, out string?
dataTypeConv);
DataTypeConversion = DataTypeConversionParser.Parse(dataTypeConv);
- Properties.TryGetValue(ImpalaParameters.TLSOptions, out string?
tlsOptions);
+ TlsOptions = HiveServer2TlsImpl.GetStandardTlsOptions(Properties);
}
protected override TTransport CreateTransport()
@@ -106,8 +107,24 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Impala
// Delay the open connection until later.
bool connectClient = false;
- TSocketTransport transport = new(hostName!, int.Parse(port!),
connectClient, config: new());
- TBufferedTransport bufferedTransport = new
TBufferedTransport(transport);
+ TTransport transport;
+ if (TlsOptions.IsTlsEnabled)
+ {
+ if (IPAddress.TryParse(hostName!, out var address))
+ {
+ transport = new TTlsSocketTransport(address!,
int.Parse(port!), config: new(), 0,
!string.IsNullOrEmpty(TlsOptions.TrustedCertificatePath) ? new
X509Certificate2(TlsOptions.TrustedCertificatePath!) : null, certValidator:
HiveServer2TlsImpl.GetCertificateValidator(TlsOptions));
+ }
+ else
+ {
+ transport = new TTlsSocketTransport(hostName!,
int.Parse(port!), config: new(), 0,
!string.IsNullOrEmpty(TlsOptions.TrustedCertificatePath) ? new
X509Certificate2(TlsOptions.TrustedCertificatePath!) : null, certValidator:
HiveServer2TlsImpl.GetCertificateValidator(TlsOptions));
+ }
+ }
+ else
+ {
+ transport = new TSocketTransport(hostName!, int.Parse(port!),
connectClient, config: new());
+ }
+
+ TBufferedTransport bufferedTransport = new(transport);
return bufferedTransport;
}
diff --git a/csharp/src/Drivers/Apache/Impala/README.md
b/csharp/src/Drivers/Apache/Impala/README.md
index 0b287f7fe..315dbee23 100644
--- a/csharp/src/Drivers/Apache/Impala/README.md
+++ b/csharp/src/Drivers/Apache/Impala/README.md
@@ -48,11 +48,16 @@ but can also be passed in the call to
`AdbcDatabase.Connect`. Options beginning
| `adbc.get_metadata.foreign_target_catalog` | The foreign (i.e., child)
catalog name (or pattern) when used with a metadata command query.
<br><br>Supported metadata commands include: `GetCrossReference`. | |
| `adbc.get_metadata.foreign_target_db_schema` | The foreign (i.e., child)
schema name (or pattern) when used with a metadata command query.
<br><br>Supported metadata commands include: `GetCrossReference`. | |
| `adbc.get_metadata.foreign_target_table` | The foreign (i.e., child) table
name (or pattern) when used with a metadata command query. <br><br>Supported
metadata commands include: `GetCrossReference`. | |
-| `adbc.http_options.tls.enabled` | If tls needs to enabled or not. One of
`True`, `False` | `False` |
+| `adbc.http_options.tls.enabled` | If tls needs to enabled or not for http
transport type. One of `True`, `False` | `True` |
| `adbc.http_options.tls.disable_server_certificate_validation` | If tls/ssl
server certificate validation needs to enabled or not. One of `True`, `False`.
If set to True, all certificate validation errors are ignored | `False` |
| `adbc.http_options.tls.allow_self_signed` | If self signed tls/ssl
certificate needs to be allowed or not. One of `True`, `False` | `False` |
| `adbc.http_options.tls.allow_hostname_mismatch` | If hostname mismatch is
allowed for ssl. One of `True`, `False` | `False` |
-| `adbc.http_options.tls.trusted_certificate_path` | The full path of the
tls/ssl certificate .pem file containing custom CA certificates for verifying
the server when connecting over TLS | `` |
+| `adbc.http_options.tls.trusted_certificate_path` | The full path of the
tls/ssl certificate .pem file containing custom CA certificates for verifying
the server when connecting over TLS | |
+| `adbc.standard_options.tls.enabled` | If tls needs to enabled or not for
standard transport type. One of `True`, `False` | `True` |
+| `adbc.standard_options.tls.disable_server_certificate_validation` | If
tls/ssl server certificate validation needs to enabled or not. One of `True`,
`False`. If set to True, all certificate validation errors are ignored |
`False` |
+| `adbc.standard_options.tls.allow_self_signed` | If self signed tls/ssl
certificate needs to be allowed or not. One of `True`, `False` | `False` |
+| `adbc.standard_options.tls.allow_hostname_mismatch` | If hostname mismatch
is allowed for ssl. One of `True`, `False` | `False` |
+| `adbc.standard_options.tls.trusted_certificate_path` | The full path of the
tls/ssl certificate .pem file containing custom CA certificates for verifying
the server when connecting over TLS | |
## Timeout Configuration
diff --git a/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs
b/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs
index 70d5534e6..f707b992f 100644
--- a/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs
+++ b/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs
@@ -26,8 +26,8 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Apache.Hive2
public class HiveServer2TlsImplTest
{
[SkippableTheory]
- [MemberData(nameof(GetSslOptionsTestData))]
- internal void TestValidateTlsOptions(Dictionary<string, string>?
dataTypeConversion, TlsProperties expected, Type? exceptionType = default)
+ [MemberData(nameof(GetHttpTlsOptionsTestData))]
+ internal void TestValidateHttpTlsOptions(Dictionary<string, string>?
dataTypeConversion, TlsProperties expected, Type? exceptionType = default)
{
if (exceptionType == default)
Assert.Equivalent(expected,
HiveServer2TlsImpl.GetHttpTlsOptions(dataTypeConversion ?? new
Dictionary<string, string>()));
@@ -35,8 +35,22 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Apache.Hive2
Assert.Throws(exceptionType, () =>
HiveServer2TlsImpl.GetHttpTlsOptions(dataTypeConversion ?? new
Dictionary<string, string>()));
}
- public static IEnumerable<object?[]> GetSslOptionsTestData()
+ [SkippableTheory]
+ [MemberData(nameof(GetStandardTlsOptionsTestData))]
+ internal void TestValidateStandardTlsOptions(Dictionary<string,
string>? dataTypeConversion, TlsProperties expected, Type? exceptionType =
default)
+ {
+ if (exceptionType == default)
+ Assert.Equivalent(expected,
HiveServer2TlsImpl.GetStandardTlsOptions(dataTypeConversion ?? new
Dictionary<string, string>()));
+ else
+ Assert.Throws(exceptionType, () =>
HiveServer2TlsImpl.GetStandardTlsOptions(dataTypeConversion ?? new
Dictionary<string, string>()));
+ }
+
+ public static IEnumerable<object?[]> GetHttpTlsOptionsTestData()
{
+ // Tls is enabled by default
+ yield return new object?[] { new Dictionary<string, string> { },
new TlsProperties { IsTlsEnabled = true } };
+ yield return new object?[] { new Dictionary<string, string> { {
HttpTlsOptions.IsTlsEnabled, "abc" } }, new TlsProperties { IsTlsEnabled = true
} };
+
yield return new object?[] { new Dictionary<string, string> { {
HttpTlsOptions.IsTlsEnabled, "False" } }, new TlsProperties { IsTlsEnabled =
false } };
yield return new object?[] { new Dictionary<string, string> { {
AdbcOptions.Uri, "https://arrow.apache.org" } }, new TlsProperties {
IsTlsEnabled = true, DisableServerCertificateValidation = false } };
// uri takes precedence over ssl option
@@ -55,5 +69,27 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Apache.Hive2
// invalid certificate path
yield return new object?[] { new Dictionary<string, string> { {
HttpTlsOptions.IsTlsEnabled, "True" }, { HttpTlsOptions.AllowSelfSigned, "True"
}, { HttpTlsOptions.AllowHostnameMismatch, "True" }, {
HttpTlsOptions.TrustedCertificatePath, "" } }, null,
typeof(FileNotFoundException) };
}
+
+ public static IEnumerable<object?[]> GetStandardTlsOptionsTestData()
+ {
+ // Tls is enabled by default
+ yield return new object?[] { new Dictionary<string, string> { },
new TlsProperties { IsTlsEnabled = true } };
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "abc" } }, new TlsProperties { IsTlsEnabled =
true } };
+
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "False" } }, new TlsProperties { IsTlsEnabled
= false } };
+ // other ssl options are ignored if
disableServerCertificateValidation is set to true
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "True" }, {
StandardTlsOptions.DisableServerCertificateValidation, "True" }, {
StandardTlsOptions.AllowSelfSigned, "True" }, {
StandardTlsOptions.AllowHostnameMismatch, "True" } }, new TlsProperties {
IsTlsEnabled = true, DisableServerCertificateValidation = true } };
+ // other ssl options are ignored if ssl is disabled
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "False" }, {
StandardTlsOptions.AllowSelfSigned, "True" }, {
StandardTlsOptions.AllowHostnameMismatch, "True" } }, new TlsProperties {
IsTlsEnabled = false } };
+ // case insensitive boolean string parsing
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "false" } }, new TlsProperties { IsTlsEnabled
= false } };
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "True" } }, new TlsProperties { IsTlsEnabled =
true, DisableServerCertificateValidation = false, AllowSelfSigned = false,
AllowHostnameMismatch = false } };
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "tRUe" }, {
StandardTlsOptions.AllowSelfSigned, "true" } }, new TlsProperties {
IsTlsEnabled = true, DisableServerCertificateValidation = false,
AllowSelfSigned = true, AllowHostnameMismatch = false } };
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "TruE" }, {
StandardTlsOptions.AllowSelfSigned, "True" }, {
StandardTlsOptions.AllowHostnameMismatch, "True" } }, new TlsProperties {
IsTlsEnabled = true, DisableServerCertificateValidation = false,
AllowSelfSigned = true, AllowHostnameMismatch = true } };
+ // certificate path is ignored if self signed is not allowed
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "True" }, {
StandardTlsOptions.AllowSelfSigned, "False" }, {
StandardTlsOptions.AllowHostnameMismatch, "True" }, {
StandardTlsOptions.TrustedCertificatePath, "" } }, new TlsProperties {
IsTlsEnabled = true, DisableServerCertificateValidation = false,
AllowSelfSigned = false, AllowHostnameMismatch = true } };
+ // invalid certificate path
+ yield return new object?[] { new Dictionary<string, string> { {
StandardTlsOptions.IsTlsEnabled, "True" }, {
StandardTlsOptions.AllowSelfSigned, "True" }, {
StandardTlsOptions.AllowHostnameMismatch, "True" }, {
StandardTlsOptions.TrustedCertificatePath, "" } }, null,
typeof(FileNotFoundException) };
+ }
}
}