From 8fa5df595351628e8a8da0e0cf3e10cd9a29796d Mon Sep 17 00:00:00 2001 From: Juan Martinez Ramirez <126511805+sfc-gh-jmartinez@users.noreply.github.com> Date: Wed, 20 Mar 2024 07:28:19 -0600 Subject: [PATCH] SNOW-1230345 Fix to support quoted values in object connection properties (#891) ### Description Fix to support quoted values in object connection properties. Supported object properties with scaped quotes: - DB - SCHEMA - ROLE - WAREHOUSE ### Checklist - [x] Code compiles correctly - [x] Code is formatted according to [Coding Conventions](../CodingConventions.md) - [x] Created tests which fail without the change (if possible) - [x] All tests passing (`dotnet test`) - [x] Extended the README / documentation, if necessary - [x] Provide JIRA issue id (if possible) or GitHub issue id in PR name --- .../IntegrationTests/SFConnectionIT.cs | 4 +- .../UnitTests/SFSessionPropertyTest.cs | 43 ++++++++- .../Session/SFHttpClientPropertiesTest.cs | 2 +- .../SFHttpClientProxyPropertiesTest.cs | 2 +- Snowflake.Data/Core/Session/SFSession.cs | 2 +- .../Core/Session/SFSessionProperty.cs | 88 +++++++++++-------- 6 files changed, 96 insertions(+), 45 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs index 816e064b3..8d69fe606 100644 --- a/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs @@ -1652,7 +1652,7 @@ public void testMulitpleConnectionInParallel() } [Test] - [Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::parseConnectionString() to verify")] + [Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::ParseConnectionString() to verify")] public void TestEscapeChar() { using (IDbConnection conn = new SnowflakeDbConnection()) @@ -1679,7 +1679,7 @@ public void TestEscapeChar() } [Test] - [Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::parseConnectionString() to verify")] + [Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::ParseConnectionString() to verify")] public void TestEscapeChar1() { using (IDbConnection conn = new SnowflakeDbConnection()) diff --git a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs index 694af9b5e..4b2e3ec8f 100644 --- a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs @@ -11,6 +11,7 @@ namespace Snowflake.Data.Tests.UnitTests { + class SFSessionPropertyTest { @@ -18,7 +19,7 @@ class SFSessionPropertyTest public void TestThatPropertiesAreParsed(TestCase testcase) { // act - var properties = SFSessionProperties.parseConnectionString( + var properties = SFSessionProperties.ParseConnectionString( testcase.ConnectionString, testcase.SecurePassword); @@ -40,7 +41,7 @@ public void TestValidateCorrectAccountNames(string accountName, string expectedA var connectionString = $"ACCOUNT={accountName};USER=test;PASSWORD=test;"; // act - var properties = SFSessionProperties.parseConnectionString(connectionString, null); + var properties = SFSessionProperties.ParseConnectionString(connectionString, null); // assert Assert.AreEqual(expectedAccountName, properties[SFSessionProperty.ACCOUNT]); @@ -60,7 +61,7 @@ public void TestThatItFailsForWrongConnectionParameter(string connectionString, { // act var exception = Assert.Throws( - () => SFSessionProperties.parseConnectionString(connectionString, null) + () => SFSessionProperties.ParseConnectionString(connectionString, null) ); // assert @@ -75,13 +76,46 @@ public void TestThatItFailsIfNoAccountSpecified(string connectionString) { // act var exception = Assert.Throws( - () => SFSessionProperties.parseConnectionString(connectionString, null) + () => SFSessionProperties.ParseConnectionString(connectionString, null) ); // assert Assert.AreEqual(SFError.MISSING_CONNECTION_PROPERTY.GetAttribute().errorCode, exception.ErrorCode); } + + + [Test] + [TestCase("DB", SFSessionProperty.DB, "\"testdb\"")] + [TestCase("SCHEMA", SFSessionProperty.SCHEMA, "\"quotedSchema\"")] + [TestCase("ROLE", SFSessionProperty.ROLE, "\"userrole\"")] + [TestCase("WAREHOUSE", SFSessionProperty.WAREHOUSE, "\"warehouse test\"")] + public void TestValidateSupportEscapedQuotesValuesForObjectProperties(string propertyName, SFSessionProperty sessionProperty, string value) + { + // arrange + var connectionString = $"ACCOUNT=test;{propertyName}={value};USER=test;PASSWORD=test;"; + + // act + var properties = SFSessionProperties.ParseConnectionString(connectionString, null); + + // assert + Assert.AreEqual(value, properties[sessionProperty]); + } + + [Test] + public void TestProcessEmptyUserAndPasswordInConnectionString() + { + // arrange + var connectionString = $"ACCOUNT=test;USER=;PASSWORD=;"; + + // act + var properties = SFSessionProperties.ParseConnectionString(connectionString, null); + + // assert + Assert.AreEqual(string.Empty, properties[SFSessionProperty.USER]); + Assert.AreEqual(string.Empty, properties[SFSessionProperty.PASSWORD]); + } + public static IEnumerable ConnectionStringTestCases() { string defAccount = "testaccount"; @@ -134,6 +168,7 @@ public static IEnumerable ConnectionStringTestCases() { SFSessionProperty.ALLOWUNDERSCORESINHOST, defAllowUnderscoresInHost } } }; + var testCaseWithBrowserResponseTimeout = new TestCase() { ConnectionString = $"ACCOUNT={defAccount};BROWSER_RESPONSE_TIMEOUT=180;authenticator=externalbrowser", diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs index 72421f588..617e3d429 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientPropertiesTest.cs @@ -115,7 +115,7 @@ public void TestExtractProperties(PropertiesTestCase testCase) // arrange var proxyExtractorMock = new Moq.Mock(); var extractor = new SFSessionHttpClientProperties.Extractor(proxyExtractorMock.Object); - var properties = SFSessionProperties.parseConnectionString(testCase.conectionString, null); + var properties = SFSessionProperties.ParseConnectionString(testCase.conectionString, null); var proxyProperties = new SFSessionHttpClientProxyProperties(); proxyExtractorMock .Setup(e => e.ExtractProperties(properties)) diff --git a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientProxyPropertiesTest.cs b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientProxyPropertiesTest.cs index a39d9bede..53941cc27 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientProxyPropertiesTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/SFHttpClientProxyPropertiesTest.cs @@ -17,7 +17,7 @@ public void ShouldExtractProxyProperties(ProxyPropertiesTestCase testCase) { // given var extractor = new SFSessionHttpClientProxyProperties.Extractor(); - var properties = SFSessionProperties.parseConnectionString(testCase.conectionString, null); + var properties = SFSessionProperties.ParseConnectionString(testCase.conectionString, null); // when var proxyProperties = extractor.ExtractProperties(properties); diff --git a/Snowflake.Data/Core/Session/SFSession.cs b/Snowflake.Data/Core/Session/SFSession.cs index e39370f19..8f56fdda4 100755 --- a/Snowflake.Data/Core/Session/SFSession.cs +++ b/Snowflake.Data/Core/Session/SFSession.cs @@ -152,7 +152,7 @@ internal SFSession( { _easyLoggingStarter = easyLoggingStarter; connStr = connectionString; - properties = SFSessionProperties.parseConnectionString(connectionString, password); + properties = SFSessionProperties.ParseConnectionString(connectionString, password); _disableQueryContextCache = bool.Parse(properties[SFSessionProperty.DISABLEQUERYCONTEXTCACHE]); _disableConsoleLogin = bool.Parse(properties[SFSessionProperty.DISABLE_CONSOLE_LOGIN]); ValidateApplicationName(properties); diff --git a/Snowflake.Data/Core/Session/SFSessionProperty.cs b/Snowflake.Data/Core/Session/SFSessionProperty.cs index 4311fe88f..6ed45be81 100644 --- a/Snowflake.Data/Core/Session/SFSessionProperty.cs +++ b/Snowflake.Data/Core/Session/SFSessionProperty.cs @@ -160,10 +160,10 @@ public override int GetHashCode() return base.GetHashCode(); } - internal static SFSessionProperties parseConnectionString(String connectionString, SecureString password) + internal static SFSessionProperties ParseConnectionString(string connectionString, SecureString password) { logger.Info("Start parsing connection string."); - DbConnectionStringBuilder builder = new DbConnectionStringBuilder(); + var builder = new DbConnectionStringBuilder(); try { builder.ConnectionString = connectionString; @@ -175,14 +175,14 @@ internal static SFSessionProperties parseConnectionString(String connectionStrin SFError.INVALID_CONNECTION_STRING, e.Message); } - SFSessionProperties properties = new SFSessionProperties(); + var properties = new SFSessionProperties(); - string[] keys = new string[builder.Keys.Count]; - string[] values = new string[builder.Values.Count]; + var keys = new string[builder.Keys.Count]; + var values = new string[builder.Values.Count]; builder.Keys.CopyTo(keys, 0); builder.Values.CopyTo(values,0); - for(int i=0; i 0) - { - string[] tokens = keyVal.Split(new string[] { "=" }, StringSplitOptions.None); - if(tokens[0].ToUpper() == "DB" || tokens[0].ToUpper() == "SCHEMA" || - tokens[0].ToLower() == "WAREHOUSE" || tokens[0].ToUpper() == "ROLE") - { - if (tokens.Length == 2) - { - SFSessionProperty p = (SFSessionProperty)Enum.Parse( - typeof(SFSessionProperty), tokens[0].ToUpper()); - properties[p]= tokens[1]; - } - } - if(tokens[0].ToUpper() == "USER" || tokens[0].ToUpper() == "PASSWORD") - { - SFSessionProperty p = (SFSessionProperty)Enum.Parse( - typeof(SFSessionProperty), tokens[0].ToUpper()); - if (!properties.ContainsKey(p)) - { - properties.Add(p, ""); - } - } - } - } - - bool useProxy = false; + var useProxy = false; if (properties.ContainsKey(SFSessionProperty.USEPROXY)) { try @@ -292,6 +265,49 @@ internal static SFSessionProperties parseConnectionString(String connectionStrin return properties; } + private static void UpdatePropertiesForSpecialCases(SFSessionProperties properties, string connectionString) + { + var propertyEntry = connectionString.Split(';'); + foreach(var keyVal in propertyEntry) + { + if(keyVal.Length > 0) + { + var tokens = keyVal.Split(new string[] { "=" }, StringSplitOptions.None); + var propertyName = tokens[0].ToUpper(); + switch (propertyName) + { + case "DB": + case "SCHEMA": + case "WAREHOUSE": + case "ROLE": + { + if (tokens.Length == 2) + { + var sessionProperty = (SFSessionProperty)Enum.Parse( + typeof(SFSessionProperty), propertyName); + properties[sessionProperty]= tokens[1]; + } + + break; + } + case "USER": + case "PASSWORD": + { + + var sessionProperty = (SFSessionProperty)Enum.Parse( + typeof(SFSessionProperty), propertyName); + if (!properties.ContainsKey(sessionProperty)) + { + properties.Add(sessionProperty, ""); + } + + break; + } + } + } + } + } + private static void ValidateAccountDomain(SFSessionProperties properties) { var account = properties[SFSessionProperty.ACCOUNT];