diff --git a/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs index ba22c9007..7c17f9bbd 100644 --- a/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs @@ -914,6 +914,30 @@ public void TestSSOConnectionWithUser() + ";authenticator=externalbrowser;user=qa@snowflakecomputing.com"; conn.Open(); Assert.AreEqual(ConnectionState.Open, conn.State); + + // connection pooling is disabled for external browser by default + Assert.AreEqual(false, SnowflakeDbConnectionPool.GetPool(conn.ConnectionString).GetPooling()); + using (IDbCommand command = conn.CreateCommand()) + { + command.CommandText = "SELECT CURRENT_USER()"; + Assert.AreEqual("QA", command.ExecuteScalar().ToString()); + } + } + } + + [Test] + [Ignore("This test requires manual interaction and therefore cannot be run in CI")] + public void TestSSOConnectionWithPoolingEnabled() + { + // Use external browser to log in using proper password for qa@snowflakecomputing.com + using (IDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString + = ConnectionStringWithoutAuth + + ";authenticator=externalbrowser;user=qa@snowflakecomputing.com;POOLINGENABLED=TRUE"; + conn.Open(); + Assert.AreEqual(ConnectionState.Open, conn.State); + Assert.AreEqual(true, SnowflakeDbConnectionPool.GetPool(conn.ConnectionString).GetPooling()); using (IDbCommand command = conn.CreateCommand()) { command.CommandText = "SELECT CURRENT_USER()"; diff --git a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs index a0e3e8496..71af2b1c9 100644 --- a/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs @@ -139,6 +139,40 @@ public void TestValidateSupportEscapedQuotesInsideValuesForObjectProperties(stri Assert.AreEqual(expectedValue, properties[sessionProperty]); } + [Test] + [TestCase("", "false")] + [TestCase("poolingEnabled=true", "true")] + [TestCase("poolingEnabled=false", "false")] + public void TestPoolingEnabledForExternalBrowserAuthenticator(string connectionParam, string expectedPoolingEnabled) + { + // arrange + var connectionString = $"ACCOUNT=test;AUTHENTICATOR=externalbrowser;{connectionParam}"; + + // act + var properties = SFSessionProperties.ParseConnectionString(connectionString, null); + + // assert + Assert.AreEqual(expectedPoolingEnabled, properties[SFSessionProperty.POOLINGENABLED]); + } + + [Test] + [TestCase(BasicAuthenticator.AUTH_NAME, "true")] + [TestCase(KeyPairAuthenticator.AUTH_NAME, "true")] + [TestCase(OAuthAuthenticator.AUTH_NAME, "true")] + [TestCase(OktaAuthenticator.AUTH_NAME, "true")] + [TestCase(ExternalBrowserAuthenticator.AUTH_NAME, "false")] + public void TestDefaultPoolingEnabledForAuthenticator(string authenticator, string expectedPoolingEnabled) + { + // arrange + var connectionString = $"ACCOUNT=test;USER=test;PASSWORD=test;TOKEN=test;AUTHENTICATOR={authenticator}"; + + // act + var properties = SFSessionProperties.ParseConnectionString(connectionString, null); + + // assert + Assert.AreEqual(expectedPoolingEnabled, properties[SFSessionProperty.POOLINGENABLED]); + } + public static IEnumerable ConnectionStringTestCases() { string defAccount = "testaccount"; @@ -229,7 +263,7 @@ public static IEnumerable ConnectionStringTestCases() { SFSessionProperty.CHANGEDSESSION, DefaultValue(SFSessionProperty.CHANGEDSESSION) }, { SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT, DefaultValue(SFSessionProperty.WAITINGFORIDLESESSIONTIMEOUT) }, { SFSessionProperty.EXPIRATIONTIMEOUT, DefaultValue(SFSessionProperty.EXPIRATIONTIMEOUT) }, - { SFSessionProperty.POOLINGENABLED, DefaultValue(SFSessionProperty.POOLINGENABLED) } + { SFSessionProperty.POOLINGENABLED, "false" } // connection pooling is disabled for external browser authentication } }; var testCaseWithProxySettings = new TestCase() @@ -616,6 +650,7 @@ public static IEnumerable ConnectionStringTestCases() private static string DefaultValue(SFSessionProperty property) => property.GetAttribute().defaultValue; + internal class TestCase { public string ConnectionString { get; set; } diff --git a/Snowflake.Data/Core/Session/SFSessionProperty.cs b/Snowflake.Data/Core/Session/SFSessionProperty.cs index 0b598c35f..c2e3777b6 100644 --- a/Snowflake.Data/Core/Session/SFSessionProperty.cs +++ b/Snowflake.Data/Core/Session/SFSessionProperty.cs @@ -254,6 +254,7 @@ internal static SFSessionProperties ParseConnectionString(string connectionStrin } ValidateAuthenticator(properties); + DisableConnectionPoolingForExternalBrowser(properties); CheckSessionProperties(properties); ValidateFileTransferMaxBytesInMemoryProperty(properties); ValidateAccountDomain(properties); @@ -287,7 +288,14 @@ internal static SFSessionProperties ParseConnectionString(string connectionStrin private static void ValidateAuthenticator(SFSessionProperties properties) { - var knownAuthenticators = new[] { BasicAuthenticator.AUTH_NAME, OktaAuthenticator.AUTH_NAME, OAuthAuthenticator.AUTH_NAME, KeyPairAuthenticator.AUTH_NAME, ExternalBrowserAuthenticator.AUTH_NAME }; + var knownAuthenticators = new[] { + BasicAuthenticator.AUTH_NAME, + OktaAuthenticator.AUTH_NAME, + OAuthAuthenticator.AUTH_NAME, + KeyPairAuthenticator.AUTH_NAME, + ExternalBrowserAuthenticator.AUTH_NAME + }; + if (properties.TryGetValue(SFSessionProperty.AUTHENTICATOR, out var authenticator)) { authenticator = authenticator.ToLower(); @@ -300,6 +308,26 @@ private static void ValidateAuthenticator(SFSessionProperties properties) } } + private static void DisableConnectionPoolingForExternalBrowser(SFSessionProperties properties) + { + if (properties.TryGetValue(SFSessionProperty.AUTHENTICATOR, out var authenticator)) + { + authenticator = authenticator.ToLower(); + if (authenticator.Equals(ExternalBrowserAuthenticator.AUTH_NAME)) + { + if (!properties.TryGetValue(SFSessionProperty.POOLINGENABLED, out var poolingEnabledStr)) + { + properties.Add(SFSessionProperty.POOLINGENABLED, "false"); + logger.Info("Connection pooling is disabled for external browser authentication"); + } + else if (Boolean.TryParse(poolingEnabledStr, out var poolingEnabled) && poolingEnabled) + { + logger.Warn("Connection pooling is enabled for external browser authentication"); + } + } + } + } + private static string BuildConnectionStringWithoutSecrets(ref string[] keys, ref string[] values) { var count = keys.Length;