From 5c505a1955f2e2966f58bad32011348b2d9de143 Mon Sep 17 00:00:00 2001 From: Aris Rellegue Date: Thu, 8 Jun 2023 10:43:57 -0700 Subject: [PATCH] Add SqlConnectionEncryptOptionConverter class which is used to convert string Encrypt option into SqlConnectionEncryptionOption type. --- .../SqlConnectionEncryptOptionConverter.xml | 51 ++++++++++ src/Microsoft.Data.SqlClient.sln | 1 + .../src/Microsoft.Data.SqlClient.csproj | 3 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + .../SqlClient/SqlConnectionEncryptOption.cs | 2 + .../SqlConnectionEncryptOptionConverter.cs | 46 +++++++++ .../Microsoft.Data.SqlClient.Tests.csproj | 1 + .../SqlConnectionStringBuilderTest.cs | 93 +++++++++++++++++++ 8 files changed, 200 insertions(+) create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml new file mode 100644 index 0000000000..9f26bc71c7 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOptionConverter.xml @@ -0,0 +1,51 @@ + + + + + Converts a string Sql Connection Encrypt option into SqlConnectionEncryptOption object + + + ## Remarks +Implicit conversions have been added to maintain backwards compatibility with boolean behahavior for the property. When converting from a boolean, a value of `true` converts to and a value of `false` converts to . When converting to a boolean, , , and `null` convert to `true` and converts `false`. + + + + + + If the source type is a string then conversion is allowed . + + A string containing the value to convert. + + if the parameter can be converted successfully; otherwise, . + + This method does not throw an exception. + + + + Converts the specified string representation of a logical value to its equivalent. + + A string containing the value to convert. + + An object that is equivalent to . + + + An object that is equivalent to with value of if conversion was successful; + otherwise, an exception is thrown. + + This method throws an exception if conversion fails. + + + + Converts an object value to its string representation. + + An object containing the value to convert. + + A string representation of the value of . + + + A string representation of the value of . + + This method does not throw an exception if conversion fails. + + + diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index c5b34945a0..a94447bf12 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -121,6 +121,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOptionConverter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOptionConverter.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index be1eb19b17..41f8374191 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -330,6 +330,9 @@ Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 4492274116..675dd57fe6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -422,6 +422,9 @@ Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs index ecabdb9f04..997833437f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { /// + [TypeConverter(typeof(SqlConnectionEncryptOptionConverter))] public sealed class SqlConnectionEncryptOption { private const string TRUE = "True"; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs new file mode 100644 index 0000000000..da5eb6f8a1 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Drawing; + +namespace Microsoft.Data.SqlClient +{ + /// + public class SqlConnectionEncryptOptionConverter : TypeConverter + { + // Overrides the CanConvertFrom method of TypeConverter. + // The ITypeDescriptorContext interface provides the context for the + // conversion. Typically, this interface is used at design time to + // provide information about the design-time container. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + // Overrides the ConvertFrom method of TypeConverter. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return SqlConnectionEncryptOption.Parse(value.ToString()); + } + throw new Exception("Value to convert must be of string type!"); + } + // Overrides the ConvertTo method of TypeConverter. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + return base.ConvertTo(context, culture, value, destinationType); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 9d1e8d5087..408d7ffa61 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -66,6 +66,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 2bc842566a..591a426568 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -4,6 +4,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Text; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -468,6 +474,36 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value) Assert.Null(result); } + [Theory] + [InlineData("false","False")] + [InlineData("true", "True")] + [InlineData("strict", "Strict")] + [InlineData("mandatory","True")] + [InlineData("optional", "False")] + [InlineData("yes", "True")] + [InlineData("no", "False")] + [InlineData("absolutely", "True")] + [InlineData("affirmative", "True")] + [InlineData("never", "True")] + [InlineData("always", "True")] + [InlineData("none", "True")] + public void ConnectionStringFromJsonTests(string value, string expectedValue) + { + ExecuteConnectionStringFromJsonTests(value, expectedValue); + } + + [Theory] + [InlineData("absolutely")] + [InlineData("affirmative")] + [InlineData("never")] + [InlineData("always")] + [InlineData("none")] + [InlineData(" for sure ")] + public void ConnectionStringFromJsonThrowsException(string value) + { + ExecuteConnectionStringFromJsonThrowsException(value); + } + internal void ExecuteConnectionStringTests(string connectionString) { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString); @@ -495,5 +531,62 @@ internal static void CheckEncryptType(SqlConnectionStringBuilder builder, SqlCon Assert.IsType(builder.Encrypt); Assert.Equal(expectedValue, builder.Encrypt); } + + internal void ExecuteConnectionStringFromJsonTests(string encryptOption, string result) + { + var settings = LoadSettingsFromJsonStream(encryptOption); + var connectionString = settings!.UserDb!.ToString(); + Assert.Contains($"Encrypt={result}", connectionString, StringComparison.InvariantCultureIgnoreCase); + } + + internal void ExecuteConnectionStringFromJsonThrowsException(string encryptOption) + { + Assert.Throws(() => LoadSettingsFromJsonStream(encryptOption)); + } + + TSettings LoadSettingsFromJsonStream(string encryptOption) where TSettings : class + { + TSettings settingsOut = null; + + Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((ctx, configBuilder) => + { + // Note: Inside string interpolation, a { should be {{ and a } should be }} + // First, declare a stringified JSON + var json = $"{{ \"UserDb\": {{ \"UserComponents\": {{ \"NetworkLibrary\": \"DBMSSOCN\", \"UserID\": \"user\", \"Password\": \"password\", \"DataSource\": \"localhost\", \"InitialCatalog\": \"catalog\", \"Encrypt\": \"{encryptOption}\" }}}}}}"; + + // Load the stringified JSON as a stream into the configuration builder + configBuilder.AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json))); + configBuilder.AddEnvironmentVariables(); + }) + .ConfigureServices((ctx, services) => + { + var configuration = ctx.Configuration; + services.AddOptions(); + services.Configure(ctx.Configuration); + settingsOut = configuration.Get(); + }) + .Build(); + + return settingsOut; + } + } + + // These 2 classes will be used by ConnectionStringFromJsonTests only + internal class UserDbConnectionStringSettings + { + [Required] + public UserSqlConnectionString UserDb { get; set; } + } + + internal class UserSqlConnectionString + { + public SqlConnectionStringBuilder UserComponents { get; set; } = new(); + + public override string ToString() + { + return UserComponents!.ConnectionString; + } } + }