Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emulator : Adds support for flag in connection string to ignore SSL check #4251

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Microsoft.Azure.Cosmos/src/CosmosClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ protected CosmosClient()
/// </example>
/// <remarks>
/// The returned reference doesn't guarantee credentials or connectivity validations because creation doesn't make any network calls.
/// With emulator to ignore SSL Certificate please use connectionstring with "DisableServerCertificateValidation" flag.
/// This flag will be overrided for HTTP calls if custom HttpClientFactory is used.
/// It is NOT recommended to use this flag in production.
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
/// </remarks>
/// <seealso cref="CosmosClientOptions"/>
/// <seealso cref="Fluent.CosmosClientBuilder"/>
Expand All @@ -195,7 +198,7 @@ public CosmosClient(
: this(
CosmosClientOptions.GetAccountEndpoint(connectionString),
CosmosClientOptions.GetAccountKey(connectionString),
clientOptions)
CosmosClientOptions.GetCosmosClientOptionsWithCertificateFlag(connectionString, clientOptions))
{
}

Expand Down Expand Up @@ -495,6 +498,11 @@ public static async Task<CosmosClient> CreateAndInitializeAsync(string accountEn
/// ]]>
/// </code>
/// </example>
/// <remarks>
/// With emulator to ignore SSL Certificate please use connectionstring with "DisableServerCertificateValidation" flag.
/// This flag will be overrided for HTTP calls if custom HttpClientFactory is used.
/// It is NOT recommended to use this flag in production.
/// </remarks>
public static async Task<CosmosClient> CreateAndInitializeAsync(string connectionString,
IReadOnlyList<(string databaseId, string containerId)> containers,
CosmosClientOptions cosmosClientOptions = null,
Expand All @@ -504,6 +512,7 @@ public static async Task<CosmosClient> CreateAndInitializeAsync(string connectio
{
throw new ArgumentNullException(nameof(containers));
}
cosmosClientOptions = CosmosClientOptions.GetCosmosClientOptionsWithCertificateFlag(connectionString, cosmosClientOptions);

CosmosClient cosmosClient = new CosmosClient(connectionString,
cosmosClientOptions);
Expand Down
85 changes: 57 additions & 28 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class CosmosClientOptions

private const string ConnectionStringAccountEndpoint = "AccountEndpoint";
private const string ConnectionStringAccountKey = "AccountKey";
private const string ConnectionStringDisableServerCertificateValidation = "DisableServerCertificateValidation";

private const ApiType DefaultApiType = ApiType.None;

Expand Down Expand Up @@ -843,34 +844,62 @@ internal virtual ConnectionPolicy GetConnectionPolicy(int clientId)
return (Documents.ConsistencyLevel)this.ConsistencyLevel.Value;
}

internal static string GetAccountEndpoint(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountEndpoint);
}

internal static string GetAccountKey(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountKey);
}

private static string GetValueFromConnectionString(string connectionString, string keyName)
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}

DbConnectionStringBuilder builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
if (builder.TryGetValue(keyName, out object value))
{
string keyNameValue = value as string;
if (!string.IsNullOrEmpty(keyNameValue))
{
return keyNameValue;
}
}

throw new ArgumentException("The connection string is missing a required property: " + keyName);
internal static string GetAccountEndpoint(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString<string>(connectionString, CosmosClientOptions.ConnectionStringAccountEndpoint, null);
}

internal static string GetAccountKey(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString<string>(connectionString, CosmosClientOptions.ConnectionStringAccountKey, null);
}

internal static bool IsConnectionStringDisableServerCertificateValidationFlag(string connectionString)
{
return Convert.ToBoolean(CosmosClientOptions.GetValueFromConnectionString<bool>(connectionString, CosmosClientOptions.ConnectionStringDisableServerCertificateValidation, false));
}

internal static CosmosClientOptions GetCosmosClientOptionsWithCertificateFlag(string connectionString, CosmosClientOptions clientOptions)
{
clientOptions ??= new CosmosClientOptions();
if (CosmosClientOptions.IsConnectionStringDisableServerCertificateValidationFlag(connectionString))
{
clientOptions.ServerCertificateCustomValidationCallback = (_, _, _) => true;
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}

return clientOptions;
}

private static T GetValueFromConnectionString<T>(string connectionString, string keyName, T defaultValue)
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}

DbConnectionStringBuilder builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
if (builder.TryGetValue(keyName, out object value))
{
string keyNameValue = value as string;
if (!string.IsNullOrEmpty(keyNameValue))
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (InvalidCastException)
{
throw new ArgumentException("The connection string contains invalid property: " + keyName);
}
}
}

if (defaultValue != null)
{
return defaultValue;
}

throw new ArgumentException("The connection string is missing a required property: " + keyName);
}

private void ValidateLimitToEndpointSettings()
Expand Down
6 changes: 6 additions & 0 deletions Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public CosmosClientBuilder(
/// </summary>
/// <example>"AccountEndpoint=https://mytestcosmosaccount.documents.azure.com:443/;AccountKey={SecretAccountKey};"</example>
/// <param name="connectionString">The connection string must contain AccountEndpoint and AccountKey or ResourceToken.</param>
/// <remarks>With emulator to ignore SSL Certificate please use connectionstring with "DisableServerCertificateValidation" flag.
/// This flag will be overrided for HTTP calls if custom HttpClientFactory is used.
/// It is NOT recommended to use this flag in production.
/// </remarks>
public CosmosClientBuilder(string connectionString)
{
if (connectionString == null)
Expand All @@ -133,6 +137,8 @@ public CosmosClientBuilder(string connectionString)

this.accountEndpoint = CosmosClientOptions.GetAccountEndpoint(connectionString);
this.accountKey = CosmosClientOptions.GetAccountKey(connectionString);

this.clientOptions = CosmosClientOptions.GetCosmosClientOptionsWithCertificateFlag(connectionString, this.clientOptions);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Tests
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.Http;
Expand Down Expand Up @@ -885,7 +884,24 @@ public void InvalidApplicationNameCatchTest()
ApplicationName = illegal
});
}
}
}
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved

[TestMethod]
[DataRow(ConnectionString, false)]
[DataRow(ConnectionString + "DisableServerCertificateValidation=true;", true)]
public void TestServerCertificatesValidationCallback(string connStr, bool expectedIgnoreCertificateFlag)
{
CosmosClient cosmosClient = new CosmosClient(connStr);

if (expectedIgnoreCertificateFlag)
{
Assert.IsNotNull(cosmosClient.ClientOptions.ServerCertificateCustomValidationCallback);
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
Assert.IsNull(cosmosClient.ClientOptions.ServerCertificateCustomValidationCallback);
}
}

private class TestWebProxy : IWebProxy
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,31 @@ public void WithServerCertificateAddedClientOptions_CreateContext_RemoteCertific

}

[TestMethod]
public void WithServerCertificateIgnoreFlagAddedInEndpoint_CreateContext_RemoteCertificateCallbackReturnsTrue()
{
//Arrange
X509Certificate2 x509Certificate2 = new CertificateRequest("cn=www.test", ECDsa.Create(), HashAlgorithmName.SHA256).CreateSelfSigned(DateTime.Now, DateTime.Now.AddYears(1));
X509Chain x509Chain = new X509Chain();
SslPolicyErrors sslPolicyErrors = new SslPolicyErrors();

string ConnectionString = "AccountEndpoint=https://localtestcosmos.documents.azure.com:443/;AccountKey=425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ==;";
CosmosClient client = new CosmosClient(
ConnectionString + "DisableServerCertificateValidation=true;");

// Assert for HTTP and TCP calls
Assert.IsTrue(client.ClientOptions.ServerCertificateCustomValidationCallback(x509Certificate2, x509Chain, sslPolicyErrors));

//Act
CosmosClientContext context = ClientContextCore.Create(
client,
client.ClientOptions);

//Assert it for TCP calls
Assert.IsTrue(context.DocumentClient.remoteCertificateValidationCallback(new object(), x509Certificate2, x509Chain, sslPolicyErrors));

}

private CosmosClientContext CreateMockClientContext(bool allowBulkExecution = false)
{
Mock<CosmosClient> mockClient = new Mock<CosmosClient>();
Expand Down
Loading