Skip to content

Commit

Permalink
Fix validation for proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-knozderko committed Aug 1, 2024
1 parent c02b82a commit dd4d836
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 35 deletions.
53 changes: 53 additions & 0 deletions Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Snowflake.Data.Client;
using Snowflake.Data.Core.Authenticator;
using Snowflake.Data.Core.Tools;
using Snowflake.Data.Tests.Util;

namespace Snowflake.Data.Tests.UnitTests
{
Expand Down Expand Up @@ -179,6 +180,58 @@ public void TestParseAllowEmptyProxy(string connectionString, string expectedAll
Assert.AreEqual(expectedAllowEmptyProxy, properties[SFSessionProperty.ALLOWEMPTYPROXY]);
}

[Test]
public void TestFailWhenProxyConfiguredWithoutPort()
{
// arrange
var connectionString = "ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;proxyHost=localhost";

// act
var thrown = Assert.Throws<SnowflakeDbException>(() => SFSessionProperties.ParseConnectionString(connectionString, null));

// assert
SnowflakeDbExceptionAssert.HasErrorCode(thrown, SFError.MISSING_CONNECTION_PROPERTY);
Assert.That(thrown.Message, Does.Contain("Required property PROXYPORT is not provided"));
}

[Test]
public void TestFailWhenProxyUserProvidedWithoutProxyPassword()
{
// arrange
var connectionString = "ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;proxyHost=localhost;proxyPort=1234;proxyUser=testUser";

// act
var thrown = Assert.Throws<SnowflakeDbException>(() => SFSessionProperties.ParseConnectionString(connectionString, null));

// assert
SnowflakeDbExceptionAssert.HasErrorCode(thrown, SFError.MISSING_CONNECTION_PROPERTY);
Assert.That(thrown.Message, Does.Contain("Required property PROXYPASSWORD is not provided"));
}

[Test]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;proxyPort=1234;proxyPassword=xyz", SFSessionProperty.PROXYPORT)]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;proxyUser=testUser;proxyPassword=xyz", SFSessionProperty.PROXYUSER)]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;proxyPassword=xyz", SFSessionProperty.PROXYPASSWORD)]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;useProxy=true;nonProxyHosts=xyz", SFSessionProperty.NONPROXYHOSTS)]
public void TestFailWhenConfiguringProxyDetailsWithoutProxyHost(string connectionString, SFSessionProperty unwantedProperty)
{
// act
var thrown = Assert.Throws<SnowflakeDbException>(() => SFSessionProperties.ParseConnectionString(connectionString, null));

// assert
SnowflakeDbExceptionAssert.HasErrorCode(thrown, SFError.INVALID_CONNECTION_STRING);
Assert.That(thrown.Message, Does.Contain($"Proxy property {unwantedProperty.ToString()} provided while PROXYHOST is missing"));
}

[Test]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;proxyHost=localhost")]
[TestCase("ACCOUNT=account;USER=test;PASSWORD=test;useProxy=false;proxyHost=localhost;proxyPort=1234;proxyUser=testUser")]
public void TestProxyValidationsOnlyWhenProxyEnabledAndProxyHostConfigured(string connectionString)
{
// act
Assert.DoesNotThrow(() => SFSessionProperties.ParseConnectionString(connectionString, null));
}

public static IEnumerable<TestCase> ConnectionStringTestCases()
{
string defAccount = "testaccount";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Snowflake.Data.Tests.UnitTests.Session
public class SFHttpClientProxyPropertiesTest
{
[Test, TestCaseSource(nameof(ProxyPropertiesProvider))]
public void ShouldExtractProxyProperties(ProxyPropertiesTestCase testCase)
public void TestExtractProxyProperties(ProxyPropertiesTestCase testCase)
{
// given
var extractor = new SFSessionHttpClientProxyProperties.Extractor();
Expand All @@ -23,6 +23,7 @@ public void ShouldExtractProxyProperties(ProxyPropertiesTestCase testCase)
var proxyProperties = extractor.ExtractProperties(properties);

// then
Assert.AreEqual(testCase.expectedProperties.useProxy, proxyProperties.useProxy);
Assert.AreEqual(testCase.expectedProperties.proxyHost, proxyProperties.proxyHost);
Assert.AreEqual(testCase.expectedProperties.proxyPort, proxyProperties.proxyPort);
Assert.AreEqual(testCase.expectedProperties.nonProxyHosts, proxyProperties.nonProxyHosts);
Expand All @@ -37,6 +38,7 @@ public static IEnumerable<ProxyPropertiesTestCase> ProxyPropertiesProvider()
conectionString = "account=test;user=test;password=test",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = false,
proxyHost = null,
proxyPort = null,
nonProxyHosts = null,
Expand All @@ -49,6 +51,20 @@ public static IEnumerable<ProxyPropertiesTestCase> ProxyPropertiesProvider()
conectionString = "account=test;user=test;password=test;useProxy=false;proxyHost=snowflake.com;proxyPort=123;nonProxyHosts=localhost;proxyPassword=proxyPassword;proxyUser=Chris",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = false,
proxyHost = null,
proxyPort = null,
nonProxyHosts = null,
proxyPassword = null,
proxyUser = null
}
};
var proxyPropertiesConfiguredButDisabledCase2 = new ProxyPropertiesTestCase()
{
conectionString = "account=test;user=test;password=test;proxyHost=snowflake.com;proxyPort=123;nonProxyHosts=localhost;proxyPassword=proxyPassword;proxyUser=Chris",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = false,
proxyHost = null,
proxyPort = null,
nonProxyHosts = null,
Expand All @@ -58,11 +74,12 @@ public static IEnumerable<ProxyPropertiesTestCase> ProxyPropertiesProvider()
};
var proxyPropertiesConfiguredAndEnabledCase = new ProxyPropertiesTestCase()
{
conectionString = "account=test;user=test;password=test;useProxy=true;proxyHost=snowflake.com",
conectionString = "account=test;user=test;password=test;useProxy=true;proxyHost=snowflake.com;proxyPort=1234",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = true,
proxyHost = "snowflake.com",
proxyPort = null,
proxyPort = "1234",
nonProxyHosts = null,
proxyPassword = null,
proxyUser = null
Expand All @@ -74,19 +91,35 @@ public static IEnumerable<ProxyPropertiesTestCase> ProxyPropertiesProvider()
"account=test;user=test;password=test;useProxy=true;proxyHost=snowflake.com;proxyPort=123;nonProxyHosts=localhost;proxyPassword=proxyPassword;proxyUser=Chris",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = true,
proxyHost = "snowflake.com",
proxyPort = "123",
nonProxyHosts = "localhost",
proxyPassword = "proxyPassword",
proxyUser = "Chris"
}
};
var defaultProxyEnabled = new ProxyPropertiesTestCase()
{
conectionString = "account=test;user=test;password=test;useProxy=true;",
expectedProperties = new SFSessionHttpClientProxyProperties()
{
useProxy = true,
proxyHost = null,
proxyPort = null,
nonProxyHosts = null,
proxyPassword = null,
proxyUser = null
}
};
return new []
{
noProxyPropertiesCase,
proxyPropertiesConfiguredButDisabledCase,
proxyPropertiesConfiguredButDisabledCase2,
proxyPropertiesConfiguredAndEnabledCase,
proxyPropertiesAllConfiguredAndEnabled
proxyPropertiesAllConfiguredAndEnabled,
defaultProxyEnabled
};
}

Expand Down
86 changes: 55 additions & 31 deletions Snowflake.Data/Core/Session/SFSessionProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,37 +222,7 @@ internal static SFSessionProperties ParseConnectionString(string connectionStrin
}

UpdatePropertiesForSpecialCases(properties, connectionString);

var useProxy = false;
if (properties.ContainsKey(SFSessionProperty.USEPROXY))
{
try
{
useProxy = Boolean.Parse(properties[SFSessionProperty.USEPROXY]);
}
catch (Exception e)
{
// The useProxy setting is not a valid boolean value
logger.Error("Unable to connect", e);
throw new SnowflakeDbException(e,
SFError.INVALID_CONNECTION_STRING,
e.Message);
}
}

// Based on which proxy settings have been provided, update the required settings list
if (useProxy)
{
// If useProxy is true, then proxyhost and proxy port are mandatory
SFSessionProperty.PROXYHOST.GetAttribute<SFSessionPropertyAttr>().required = true;
SFSessionProperty.PROXYPORT.GetAttribute<SFSessionPropertyAttr>().required = true;

// If a username is provided, then a password is required
if (properties.ContainsKey(SFSessionProperty.PROXYUSER))
{
SFSessionProperty.PROXYPASSWORD.GetAttribute<SFSessionPropertyAttr>().required = true;
}
}
ValidateProxy(properties);

if (password != null && password.Length > 0)
{
Expand Down Expand Up @@ -298,6 +268,60 @@ internal static string ResolveConnectionAreaMessage(string host) =>
? "Connecting to CHINA Snowflake domain"
: "Connecting to GLOBAL Snowflake domain";


private static void ValidateProxy(SFSessionProperties properties)
{
var useProxy = false;
if (properties.ContainsKey(SFSessionProperty.USEPROXY))
{
try
{
useProxy = Boolean.Parse(properties[SFSessionProperty.USEPROXY]);
}
catch (Exception e)
{
// The useProxy setting is not a valid boolean value
logger.Error("Unable to connect", e);
throw new SnowflakeDbException(e,
SFError.INVALID_CONNECTION_STRING,
e.Message);
}
}

var isProxyHostProvided = properties.IsNonEmptyValueProvided(SFSessionProperty.PROXYHOST);
if (useProxy && isProxyHostProvided)
{
if (!properties.IsNonEmptyValueProvided(SFSessionProperty.PROXYPORT))
{
throw new SnowflakeDbException(SFError.MISSING_CONNECTION_PROPERTY, SFSessionProperty.PROXYPORT.ToString());
}
if (properties.IsNonEmptyValueProvided(SFSessionProperty.PROXYUSER) &&
!properties.IsNonEmptyValueProvided(SFSessionProperty.PROXYPASSWORD))
{
throw new SnowflakeDbException(SFError.MISSING_CONNECTION_PROPERTY, SFSessionProperty.PROXYPASSWORD.ToString());
}
}

if (useProxy && !isProxyHostProvided)
{
var property = FindProxyDetailOtherThanHost(properties);
if (property != null)
{
var exception = new Exception($"Proxy property {property.ToString()} provided while {SFSessionProperty.PROXYHOST.ToString()} is missing");
logger.Error("Unable to connect", exception);
throw new SnowflakeDbException(exception, SFError.INVALID_CONNECTION_STRING, exception.Message);
}
}
}

private static SFSessionProperty? FindProxyDetailOtherThanHost(SFSessionProperties properties)
{
var proxyProperties = new[] { SFSessionProperty.PROXYPORT, SFSessionProperty.PROXYUSER, SFSessionProperty.PROXYPASSWORD, SFSessionProperty.NONPROXYHOSTS }
.Where(properties.IsNonEmptyValueProvided)
.ToArray();
return proxyProperties.Length == 0 ? (SFSessionProperty?) null : proxyProperties[0];
}

private static void ValidateAuthenticator(SFSessionProperties properties)
{
var knownAuthenticators = new[] {
Expand Down

0 comments on commit dd4d836

Please sign in to comment.