diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md index df6b909f8a..c0e1dd112f 100644 --- a/.github/instructions/testing.instructions.md +++ b/.github/instructions/testing.instructions.md @@ -58,9 +58,8 @@ Copy `config.default.jsonc` to `config.jsonc` and configure: | Property | Description | |----------|-------------| -| `TCPConnectionString` | Primary TCP connection | -| `NPConnectionString` | Named Pipes connection | -| `AADPasswordConnectionString` | Entra ID password auth | +| `TCPConnectionString` | Primary TCP connection to SQL Server or Azure SQL — used for all tests. Set to an Azure SQL endpoint for Entra ID auth tests. | +| `NPConnectionString` | Named Pipes connection to on-premises SQL Server | | `AzureKeyVaultURL` | AKV for encryption tests | | `EnclaveEnabled` | Enable enclave tests | | `FileStreamDirectory` | FileStream test path | @@ -349,11 +348,14 @@ public async Task ExecuteCommand_ReturnsExpectedRows(bool async) ### DataTestUtility Common test helper class: ```csharp -DataTestUtility.TCPConnectionString // Get TCP connection -DataTestUtility.AreConnStringsSetup // Check if config exists -DataTestUtility.IsAADPasswordConnStrSetup // Check Entra ID config +DataTestUtility.TCPConnectionString // Get TCP connection string (on-prem or Azure SQL) +DataTestUtility.AreConnStringsSetup // Check if TCP/NP connection strings are configured +DataTestUtility.IsAzureConnStringSetup // Check if TCPConnectionString points to Azure SQL +DataTestUtility.IsAzureSqlConnectionString(connStr) // Detect whether any connection string targets Azure SQL ``` +For Azure SQL / Entra ID auth tests, use `TCPConnectionString.RemoveAuthAndCredsProperties()` as the base and gate the test with `IsAzureConnStringSetup`. + ### AssertExtensions Extended assertions for SqlClient: ```csharp diff --git a/TESTGUIDE.md b/TESTGUIDE.md index 791d4a7bcc..657c34b078 100644 --- a/TESTGUIDE.md +++ b/TESTGUIDE.md @@ -247,15 +247,13 @@ dotnet build -t:TestSqlClientManual -p:TestSet=2 | Property | Description | Example or notes | |----------------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------| -| `TCPConnectionString` | Connection string for a TCP-enabled SQL Server or Azure SQL database. | `Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;` | +| `TCPConnectionString` | Connection string for a TCP-enabled SQL Server or Azure SQL database. Set this to an Azure SQL endpoint to enable Entra ID authentication tests. | `Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;` or `Data Source=.database.windows.net;Database=;` | | `NPConnectionString` | Connection string for a Named Pipes-enabled SQL Server instance. | `Data Source=np:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;` | | `TCPConnectionStringHGSVBS` | Optional connection string for SQL Server with VBS enclave and HGS attestation. | Include `Attestation Protocol=HGS` and `Enclave Attestation Url`. | | `TCPConnectionStringNoneVBS` | Optional connection string for SQL Server with VBS enclave and no attestation. | Include `Attestation Protocol=None`. | | `TCPConnectionStringAASSGX` | Optional connection string for SQL Server with SGX enclave and Microsoft Azure Attestation. | Include `Attestation Protocol=AAS` and `Enclave Attestation Url`. | | `EnclaveEnabled` | Enables tests that require an enclave-configured server. | `true` or `false`. | | `TracingEnabled` | Enables tracing-related tests. | `true` or `false`. | -| `AADAuthorityURL` | Optional OAuth authority for `AADPasswordConnectionString`. | `https://login.windows.net/` | -| `AADPasswordConnectionString` | Optional connection string for Microsoft Entra ID password authentication tests. | Uses `Authentication=Active Directory Password`. | | `AADServicePrincipalId` | Optional application ID for service-principal authentication tests. | Former docs may refer to this as a secure principal ID. | | `AADServicePrincipalSecret` | Optional application secret for service-principal authentication tests. | Keep this only in local, ignored config files or secure pipeline variables. | | `AzureKeyVaultURL` | Optional Azure Key Vault URL for Always Encrypted tests. | `https://.vault.azure.net/` | diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 1b1ea7a2e7..6875ec72a4 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -243,8 +243,6 @@ jobs: TCPConnectionString: ${{ parameters.configProperties.TCPConnectionString }} ${{ if parameters.configProperties.NPConnectionString }}: NPConnectionString: ${{ parameters.configProperties.NPConnectionString }} - ${{ if parameters.configProperties.AADAuthorityURL }}: - AADAuthorityURL: ${{ parameters.configProperties.AADAuthorityURL }} ${{ if parameters.configProperties.TCPConnectionStringHGSVBS }}: TCPConnectionStringHGSVBS: ${{ parameters.configProperties.TCPConnectionStringHGSVBS }} ${{ if parameters.configProperties.TCPConnectionStringNoneVBS }}: @@ -255,8 +253,8 @@ jobs: EnclaveEnabled: ${{ eq(parameters.configProperties.EnclaveEnabled, 'true') }} ${{ if parameters.configProperties.TracingEnabled }}: TracingEnabled: ${{ eq(parameters.configProperties.TracingEnabled, 'true') }} - ${{ if parameters.configProperties.AADPasswordConnectionString }}: - AADPasswordConnectionString: ${{ parameters.configProperties.AADPasswordConnectionString }} + ${{ if parameters.configProperties.AzureSqlConnectionString }}: + AzureSqlConnectionString: ${{ parameters.configProperties.AzureSqlConnectionString }} ${{ if parameters.configProperties.AADServicePrincipalId }}: AADServicePrincipalId: ${{ parameters.configProperties.AADServicePrincipalId }} ${{ if parameters.configProperties.AADServicePrincipalSecret }}: diff --git a/eng/pipelines/common/templates/steps/update-config-file-step.yml b/eng/pipelines/common/templates/steps/update-config-file-step.yml index fd57828df0..8ac88d5fab 100644 --- a/eng/pipelines/common/templates/steps/update-config-file-step.yml +++ b/eng/pipelines/common/templates/steps/update-config-file-step.yml @@ -41,14 +41,6 @@ parameters: type: boolean default: false - - name: AADAuthorityURL - type: string - default: '' - - - name: AADPasswordConnectionString - type: string - default: '' - - name: AADServicePrincipalId type: string default: '' @@ -152,10 +144,6 @@ steps: $p.NPConnectionString="${{parameters.NPConnectionString }}" - $p.AADAuthorityURL="${{parameters.AADAuthorityURL }}" - - $p.AADPasswordConnectionString="${{parameters.AADPasswordConnectionString }}" - $p.AADServicePrincipalId="${{parameters.AADServicePrincipalId }}" $p.AADServicePrincipalSecret="${{parameters.AADServicePrincipalSecret }}" diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml index 9ee7c7132d..c0ddd26474 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-core.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml @@ -504,14 +504,12 @@ stages: configProperties: TCPConnectionString: $(AZURE_DB_TCP_CONN_STRING) NPConnectionString: $(AZURE_DB_NP_CONN_STRING) - AADAuthorityURL: $(AADAuthorityURL) # Pipeline runs against forks of the repo don't have access to Library secrets, so we # omit them entirely from the configProperties, which causes the dependent tests to be # skipped. ${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}: - AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR) AADServicePrincipalSecret: $(AADServicePrincipalSecret) - AADServicePrincipalId: $(AADServicePrincipalId) + AADServicePrincipalId: $(AADServicePrincipalId) AzureKeyVaultUrl: $(AzureKeyVaultUrl) AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) SupportsIntegratedSecurity: false @@ -535,11 +533,9 @@ stages: configProperties: TCPConnectionString: $(AZURE_DB_TCP_CONN_STRING_eastus) NPConnectionString: $(AZURE_DB_NP_CONN_STRING_eastus) - AADAuthorityURL: $(AADAuthorityURL) ${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}: - AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR_eastus) AADServicePrincipalSecret: $(AADServicePrincipalSecret) - AADServicePrincipalId: $(AADServicePrincipalId) + AADServicePrincipalId: $(AADServicePrincipalId) AzureKeyVaultUrl: $(AzureKeyVaultUrl) AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) SupportsIntegratedSecurity: false @@ -586,11 +582,9 @@ stages: configProperties: TCPConnectionString: $(AZURE_DB_TCP_CONN_STRING) NPConnectionString: $(AZURE_DB_NP_CONN_STRING) - AADAuthorityURL: $(AADAuthorityURL) ${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}: - AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR) AADServicePrincipalSecret: $(AADServicePrincipalSecret) - AADServicePrincipalId: $(AADServicePrincipalId) + AADServicePrincipalId: $(AADServicePrincipalId) AzureKeyVaultUrl: $(AzureKeyVaultUrl) AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) SupportsIntegratedSecurity: false @@ -641,7 +635,6 @@ stages: TCPConnectionStringNoneVBS: $(SQL_TCP_CONN_STRING_NoneVBS) TCPConnectionStringAASSGX: $(SQL_TCP_CONN_STRING_AASSGX) EnclaveEnabled: true - AADAuthorityURL: $(AADAuthorityURL) AADServicePrincipalId: $(AADServicePrincipalId) AADServicePrincipalSecret: $(AADServicePrincipalSecret) AzureKeyVaultUrl: $(AzureKeyVaultUrl) diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml index eb9e36152a..ad12062fc6 100644 --- a/eng/pipelines/jobs/test-azure-package-ci-job.yml +++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml @@ -264,8 +264,6 @@ jobs: # The config.jsonc file has many options, but only some of them are # used by the Azure package tests. We only specify the ones that are # necessary here. - - AADServicePrincipalId: $(AADServicePrincipalId) AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) # macOS doesn't support managed identities. ManagedIdentitySupported: ${{ not(eq(parameters.vmImage, 'macos-latest')) }} @@ -277,7 +275,7 @@ jobs: # prevents external contributors from creating PRs and running # pipelines that could expose these secrets. ${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}: - AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR) + AADServicePrincipalId: $(AADServicePrincipalId) AADServicePrincipalSecret: $(AADServicePrincipalSecret) # Perform any local SQL Server setup. diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs index 5dd3ac336e..78659bbb25 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs @@ -8,6 +8,7 @@ // new unit and/or integration tests in the future. using System.Text.RegularExpressions; +using Microsoft.Data.SqlClient.Tests.Common; namespace Microsoft.Data.SqlClient.Extensions.Azure.Test; @@ -21,28 +22,31 @@ public class AADConnectionTest public static void KustoDatabaseTest() { // This is a sample Kusto database that can be connected by any AD account. - using SqlConnection connection = new SqlConnection($"Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;User ID = {Config.UserManagedIdentityClientId};"); + using SqlConnection connection = new($"Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;User ID = {Config.UserManagedIdentityClientId};"); connection.Open(); Assert.Equal(System.Data.ConnectionState.Open, connection.State); } [ConditionalFact( typeof(Config), - nameof(Config.HasPasswordConnectionString), + nameof(Config.IsAzureSqlConnectionString), nameof(Config.HasServicePrincipal))] public static void NoCredentialsActiveDirectoryServicePrincipal() { // test Passes with correct connection string. - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + - $"Authentication=Active Directory Service Principal; User ID={Config.ServicePrincipalId}; PWD={Config.ServicePrincipalSecret};"; - ConnectAndDisconnect(connStr); + string connString = Config.TCPConnectionString + .AddServicePrincipalAuthenticationToConnString() + .AddUserToConnString(Config.ServicePrincipalId) + .AddPasswordToConnString(Config.ServicePrincipalSecret); + + ConnectAndDisconnect(connString); // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) + - "Authentication=Active Directory Service Principal;"; - InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); + string connStrWithNoCred = Config.TCPConnectionString + .AddServicePrincipalAuthenticationToConnString(); + + InvalidOperationException e = Assert.Throws + (() => ConnectAndDisconnect(connStrWithNoCred)); string expectedMessage = "Either Credential or both 'User ID' and 'Password' (or 'UID' and 'PWD') connection string keywords must be specified, if 'Authentication=Active Directory Service Principal'."; Assert.Contains(expectedMessage, e.Message); @@ -50,21 +54,24 @@ public static void NoCredentialsActiveDirectoryServicePrincipal() [ConditionalTheory( typeof(Config), - nameof(Config.HasPasswordConnectionString), + nameof(Config.IsAzureSqlConnectionString), nameof(Config.HasUserManagedIdentityClientId))] [InlineData("2445343 2343253")] [InlineData("2445343$#^@@%2343253")] public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(string userId) { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) + - $"Authentication=Active Directory Managed Identity; User Id={userId}"; + string connStrWithNoCred = Config.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString() + .AddUserToConnString(userId); SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); + // The Azure.Identity surface message can vary across SDK versions and + // platforms, so assert on the stable driver-emitted error that proves + // the managed-identity auth path was taken and failed. Regex expected = new( - @"(\[Managed Identity\]|ManagedIdentityCredential) Authentication unavailable", + @"Failed to authenticate the user.*Authentication=ActiveDirectoryManagedIdentity", RegexOptions.IgnoreCase); Assert.Matches(expected, e.GetBaseException().Message); @@ -73,13 +80,13 @@ public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(strin [ConditionalFact( typeof(Config), nameof(Config.OnAdoPool), - nameof(Config.HasPasswordConnectionString), + nameof(Config.IsAzureSqlConnectionString), nameof(Config.HasUserManagedIdentityClientId))] public static void ActiveDirectoryDefaultMustPass() { - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) + - $"Authentication=ActiveDirectoryDefault;User ID={Config.UserManagedIdentityClientId};"; + string connStr = Config.TCPConnectionString + .AddAADDefaultAuthenticationToConnString() + .AddUserToConnString(Config.UserManagedIdentityClientId); // Connection should be established using Managed Identity by default. ConnectAndDisconnect(connStr); @@ -105,9 +112,9 @@ public static void ActiveDirectoryDefaultMustPass() public static void ADIntegratedUsingSSPI() { // test Passes with correct connection string. - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; - string connStr = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys) + - $"Authentication=Active Directory Integrated;"; + string connStr = Config.TcpConnectionString + .RemoveAuthAndCredsProperties() + .AddAADIntegratedAuthenticationToConnString(); ConnectAndDisconnect(connStr); } @@ -115,25 +122,26 @@ public static void ADIntegratedUsingSSPI() typeof(Config), nameof(Config.SupportsManagedIdentity), nameof(Config.SupportsSystemAssignedManagedIdentity), - nameof(Config.HasPasswordConnectionString))] + nameof(Config.IsAzureSqlConnectionString))] public static void SystemAssigned_ManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + - $"Authentication=Active Directory Managed Identity;"; + string connStr = Config.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString(); + ConnectAndDisconnect(connStr); } [ConditionalFact( typeof(Config), nameof(Config.OnAdoPool), - nameof(Config.HasPasswordConnectionString), + nameof(Config.IsAzureSqlConnectionString), nameof(Config.HasUserManagedIdentityClientId))] public static void UserAssigned_ManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + - $"Authentication=Active Directory Managed Identity; User Id={Config.UserManagedIdentityClientId};"; + string connStr = Config.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString() + .AddUserToConnString(Config.UserManagedIdentityClientId); + ConnectAndDisconnect(connStr); } @@ -145,16 +153,14 @@ public static void UserAssigned_ManagedIdentityTest() nameof(Config.IsAzureSqlServer))] public static void Azure_SystemManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; - string connectionString = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys) - + $"Authentication=Active Directory Managed Identity;"; + string connectionString = Config.TcpConnectionString + .RemoveAuthAndCredsProperties() + .AddManagedIdentityAuthenticationToConnString(); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.Open(); + using SqlConnection conn = new(connectionString); + conn.Open(); - Assert.Equal(System.Data.ConnectionState.Open, conn.State); - } + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } [ConditionalFact( @@ -166,16 +172,15 @@ public static void Azure_SystemManagedIdentityTest() nameof(Config.IsAzureSqlServer))] public static void Azure_UserManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; - string connectionString = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys) - + $"Authentication=Active Directory Managed Identity; User Id={Config.UserManagedIdentityClientId}"; + string connectionString = Config.TcpConnectionString + .RemoveAuthAndCredsProperties() + .AddManagedIdentityAuthenticationToConnString() + .AddUserToConnString(Config.UserManagedIdentityClientId); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.Open(); + using SqlConnection conn = new(connectionString); + conn.Open(); - Assert.Equal(System.Data.ConnectionState.Open, conn.State); - } + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } // The helpers below were copied verbatim from AADConnectionTest.cs and ManualTests @@ -203,36 +208,6 @@ private static void ConnectAndDisconnect( #region Helpers from ManualTests DataTestUtility.cs - public static string RemoveKeysInConnStr(string connStr, string[] keysToRemove) - { - // tokenize connection string and remove input keys. - string res = ""; - if (connStr != null && keysToRemove != null) - { - string[] keys = connStr.Split(';'); - foreach (var key in keys) - { - if (!string.IsNullOrEmpty(key.Trim())) - { - bool removeKey = false; - foreach (var keyToRemove in keysToRemove) - { - if (key.Trim().ToLower().StartsWith(keyToRemove.Trim().ToLower(), StringComparison.Ordinal)) - { - removeKey = true; - break; - } - } - if (!removeKey) - { - res += key + ";"; - } - } - } - } - return res; - } - public static string? FetchKeyInConnStr(string connStr, string[] keys) { // tokenize connection string and find matching key diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj index 1c020c2ae4..067e31f0b5 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj +++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj @@ -47,6 +47,9 @@ Condition="'$(ReferenceType)' == 'Package'"/> + + + diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs index 1f0587685e..40e8afbcd1 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs +++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs @@ -40,8 +40,7 @@ internal static class Config internal static bool DebugEmit { get; } = false; internal static bool IntegratedSecuritySupported { get; } = false; internal static bool ManagedIdentitySupported { get; } = false; - // @TODO Remove PasswordConnectionString from config; AAD Password auth is deprecated - internal static string PasswordConnectionString { get; } = string.Empty; + internal static string TCPConnectionString { get; } = string.Empty; internal static string ServicePrincipalId { get; } = string.Empty; internal static string ServicePrincipalSecret { get; } = string.Empty; internal static string SystemAccessToken { get; } = string.Empty; @@ -56,7 +55,7 @@ internal static class Config #region Conditional Fact/Theory Helpers - internal static bool HasPasswordConnectionString() => !PasswordConnectionString.IsEmpty(); + internal static bool IsAzureSqlConnectionString() => !TCPConnectionString.IsEmpty() && IsAzureSqlServer(); internal static bool HasServicePrincipal() => !ServicePrincipalId.IsEmpty() && !ServicePrincipalSecret.IsEmpty(); internal static bool HasSystemAccessToken() => !SystemAccessToken.IsEmpty(); internal static bool HasTcpConnectionString() => !TcpConnectionString.IsEmpty(); @@ -65,7 +64,7 @@ internal static class Config internal static bool HasWorkloadIdentityFederationServiceConnectionId() => !WorkloadIdentityFederationServiceConnectionId.IsEmpty(); internal static bool IsAzureSqlServer() => - Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TcpConnectionString).DataSource); + Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TCPConnectionString).DataSource); internal static bool OnAdoPool() => AdoPool; internal static bool OnLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); @@ -117,12 +116,11 @@ static Config() // IntegratedSecuritySupported = GetBool(root, "SupportsIntegratedSecurity"); ManagedIdentitySupported = GetBool(root, "ManagedIdentitySupported"); - PasswordConnectionString = GetString(root, "AADPasswordConnectionString"); ServicePrincipalId = GetString(root, "AADServicePrincipalId"); ServicePrincipalSecret = GetString(root, "AADServicePrincipalSecret"); SystemAssignedManagedIdentitySupported = GetBool(root, "SupportsSystemAssignedManagedIdentity"); - TcpConnectionString = GetString(root, "TCPConnectionString"); + TCPConnectionString = GetString(root, "TCPConnectionString"); TenantId = GetString(root, "AzureKeyVaultTenantId"); UseManagedSniOnWindows = GetBool(root, "UseManagedSNIOnWindows"); UserManagedIdentityClientId = GetString(root, "UserManagedIdentityClientId"); @@ -163,10 +161,6 @@ string Base64Encode(string s) $" IntegratedSecuritySupported: {IntegratedSecuritySupported}"); Console.WriteLine( $" ManagedIdentitySupported: {ManagedIdentitySupported}"); - Console.WriteLine( - $" PasswordConnectionString: {PasswordConnectionString}"); - Console.WriteLine( - $" {Base64Encode(PasswordConnectionString)}"); Console.WriteLine( $" ServicePrincipalId: {ServicePrincipalId}"); Console.WriteLine( diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs index eed5c58a69..9497aa0605 100644 --- a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs @@ -3048,15 +3048,6 @@ internal static string SQL_CannotFindActiveDirectoryAuthProvider { } } - /// - /// Looks up a localized string similar to Setting 'useWamBroker' requires the 'Microsoft.Data.SqlClient.Extensions.Azure' package to expose 'Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProviderOptions'. Upgrade the 'Microsoft.Data.SqlClient.Extensions.Azure' package to a version that includes this type.. - /// - internal static string SQL_UseWamBrokerRequiresAzureExtensionUpgrade { - get { - return ResourceManager.GetString("SQL_UseWamBrokerRequiresAzureExtensionUpgrade", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cannot find an authentication provider for '{0}'.. /// @@ -4245,6 +4236,15 @@ internal static string SQL_UserInstanceFailure { } } + /// + /// Looks up a localized string similar to Setting 'useWamBroker' requires the 'Microsoft.Data.SqlClient.Extensions.Azure' package to expose 'Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProviderOptions'. Upgrade the 'Microsoft.Data.SqlClient.Extensions.Azure' package to at least v1.1.0 that includes this type.. + /// + internal static string SQL_UseWamBrokerRequiresAzureExtensionUpgrade { + get { + return ResourceManager.GetString("SQL_UseWamBrokerRequiresAzureExtensionUpgrade", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid attempt to get vector data from column '{0}'. Vectors are only supported for columns of type vector.. /// diff --git a/src/Microsoft.Data.SqlClient/tests/Common/StringExtensions.cs b/src/Microsoft.Data.SqlClient/tests/Common/StringExtensions.cs new file mode 100644 index 0000000000..42d9a93042 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/Common/StringExtensions.cs @@ -0,0 +1,216 @@ +// 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.Net; +using System.Security; +using static Microsoft.Data.SqlClient.Tests.Common.TestRandomUtilities; + +namespace Microsoft.Data.SqlClient.Tests.Common +{ + /// + /// Extension methods for string class to add connection string keywords to connection string + /// for testing connection string parsing logic + /// + public static class StringExtensions + { + /// + /// Adds a user provided or a dummy user to the connection string to test the connection string parsing logic + /// + public static string AddUserToConnString(this string connectionString, string? user = null) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + UserID = user ?? "DummyUser" + }; + return builder.ConnectionString; + } + + /// + /// Adds a user provided or randomly generated password to the connection string for + /// testing SQL Server Authentication (password auth). Not related to Entra ID Password auth, + /// which has been removed. This is used to test SQL auth scenarios and connection string parsing. + /// + public static string AddPasswordToConnString(this string connectionString, string? password = null) + { + string? pwd = password; + if (string.IsNullOrEmpty(pwd)) + { + using SecureString secureString = GenerateRandomSecureString(20); + pwd = new NetworkCredential(string.Empty, secureString).Password; + } + var builder = new SqlConnectionStringBuilder(connectionString) + { + Password = pwd + }; + return builder.ConnectionString; + } + + /// + /// Adds integrated security to the connection string to test the connection string parsing logic + /// + public static string AddIntegratedSecurityToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + IntegratedSecurity = true + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD password authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADPasswordAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { +#pragma warning disable CS0618 // ActiveDirectoryPassword is deprecated; used here only for testing legacy authentication scenarios + Authentication = SqlAuthenticationMethod.ActiveDirectoryPassword + }; +#pragma warning restore CS0618 + return builder.ConnectionString; + } + + /// + /// Adds AAD integrated authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADIntegratedAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryIntegrated + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD interactive authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADInteractiveAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryInteractive + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD device code flow authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADDeviceCodeFlowAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD service principal authentication to the connection string to test the connection string parsing logic + /// + public static string AddServicePrincipalAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryServicePrincipal + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD workload identity authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADWorkloadIdentityAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD MSI authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADMSIAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryMSI + }; + return builder.ConnectionString; + } + + /// + /// Adds AAD default authentication to the connection string to test the connection string parsing logic + /// + public static string AddAADDefaultAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryDefault + }; + return builder.ConnectionString; + } + + /// + /// Adds an invalid AAD authentication value to the connection string to test parser error handling + /// + public static string AddInvalidAADAuthenticationToConnString(this string connectionString) + { + // NOTE: Intentionally uses raw string manipulation. SqlConnectionStringBuilder validates + // the Authentication value against SqlAuthenticationMethod and would throw ArgumentException + // for "ActiveDirectoryInvalid". This method exists to test parser behavior with invalid input. + string separator = string.IsNullOrEmpty(connectionString) || connectionString.TrimEnd().EndsWith(";", StringComparison.Ordinal) + ? string.Empty : ";"; + return connectionString + separator + "Authentication=ActiveDirectoryInvalid;"; + } + + /// + /// Adds managed identity authentication to the connection string to test the connection string parsing logic + /// + public static string AddManagedIdentityAuthenticationToConnString(this string connectionString) + { + var builder = new SqlConnectionStringBuilder(connectionString) + { + Authentication = SqlAuthenticationMethod.ActiveDirectoryManagedIdentity + }; + return builder.ConnectionString; + } + + /// + /// Removes the authentication and credential properties from the connection string. The properties removed are: "Authentication", "User ID", "Password", "UID", and "PWD". + /// This is useful for testing scenarios where you want to ensure that the connection string does not contain any sensitive information related to authentication or credentials. + /// + public static string RemoveAuthAndCredsProperties(this string connectionString) + { + string[] removeKeys = ["Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security"]; + return RemoveKeysInConnStr(connectionString, removeKeys); + } + + /// + /// Removes the specified keys from the connection string using SqlConnectionStringBuilder. + /// + /// The connection string to modify. + /// The keys to remove from the connection string. + /// The modified connection string with the specified keys removed. + public static string RemoveKeysInConnStr(this string connStr, string[] keysToRemove) + { + if (connStr == null || keysToRemove == null) + { + return connStr ?? string.Empty; + } + + var builder = new SqlConnectionStringBuilder(connStr); + foreach (var key in keysToRemove) + { + builder.Remove(key); + } + + return builder.ConnectionString; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/Common/TestRandomUtilities.cs b/src/Microsoft.Data.SqlClient/tests/Common/TestRandomUtilities.cs new file mode 100644 index 0000000000..73463a787c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/Common/TestRandomUtilities.cs @@ -0,0 +1,43 @@ +// 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.Security; + +namespace Microsoft.Data.SqlClient.Tests.Common; + +public static class TestRandomUtilities +{ + // Returns a randomly generated secure string of specified length using only alphanumeric characters. + public static SecureString GenerateRandomSecureString(int length = 10) + { + const string alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + SecureString secureString = new(); + + byte[] bytes = new byte[length]; + using (System.Security.Cryptography.RandomNumberGenerator rng = System.Security.Cryptography.RandomNumberGenerator.Create()) + { + rng.GetBytes(bytes); + } + + // Map random bytes into alphanumeric characters to avoid + // connection-string delimiters such as ';' and '='. + for (int i = 0; i < length; i++) + { + secureString.AppendChar(alphanumeric[bytes[i] % alphanumeric.Length]); + } + + secureString.MakeReadOnly(); + return secureString; + } + + // Returns randomly generated characters of specified length with a prefix. + public static string GenerateRandomCharacters(string prefix, int length = 11) + { + string path = Path.GetRandomFileName(); + path = path.Replace(".", ""); // Remove period. + // Clamp length to available characters to avoid ArgumentOutOfRangeException. + return string.Concat(prefix, path.Substring(0, Math.Min(length, path.Length))); + } + +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 9bf1c0f519..13570823f4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -22,6 +22,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Identity; +using Microsoft.Data.SqlClient.Tests.Common; using Microsoft.Data.SqlClient.Tests.Common.Fixtures.DatabaseObjects; using Microsoft.Data.SqlClient.TestUtilities; using Microsoft.Identity.Client; @@ -37,8 +38,6 @@ public static class DataTestUtility public static readonly string TCPConnectionStringHGSVBS = null; public static readonly string TCPConnectionStringNoneVBS = null; public static readonly string TCPConnectionStringAASSGX = null; - public static readonly string AADAuthorityURL = null; - public static readonly string AADPasswordConnectionString = null; public static readonly string AADServicePrincipalId = null; public static readonly string AADServicePrincipalSecret = null; public static readonly string AKVOriginalUrl = null; @@ -66,9 +65,9 @@ public static class DataTestUtility public static readonly string EnclaveAzureDatabaseConnString = null; - public static bool ManagedIdentitySupported = true; + public static bool IsUserManagedIdentitySupported = false; public static string AADAccessToken = null; - public static bool SupportsSystemAssignedManagedIdentity = false; + public static bool IsSystemManagedIdentitySupported = false; public static string AADSystemIdentityAccessToken = null; public static string AADUserIdentityAccessToken = null; public const string ApplicationClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; @@ -253,8 +252,6 @@ static DataTestUtility() TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; TCPConnectionStringNoneVBS = c.TCPConnectionStringNoneVBS; TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX; - AADAuthorityURL = c.AADAuthorityURL; - AADPasswordConnectionString = c.AADPasswordConnectionString; AADServicePrincipalId = c.AADServicePrincipalId; AADServicePrincipalSecret = c.AADServicePrincipalSecret; LocalDbAppName = c.LocalDbAppName; @@ -274,7 +271,8 @@ static DataTestUtility() PowerShellPath = c.PowerShellPath; KerberosDomainPassword = c.KerberosDomainPassword; KerberosDomainUser = c.KerberosDomainUser; - ManagedIdentitySupported = c.ManagedIdentitySupported; + IsUserManagedIdentitySupported = c.ManagedIdentitySupported && !string.IsNullOrEmpty(UserManagedIdentityClientId); + IsSystemManagedIdentitySupported = c.ManagedIdentitySupported; IsManagedInstance = c.IsManagedInstance; AliasName = c.AliasName; @@ -282,6 +280,13 @@ static DataTestUtility() System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; #endif + if(IsAzureSqlConnectionString(TCPConnectionString)) + { + // TEMP: Remove authentication and credential properties from the connection string + // if connection string is fetched from variable group that hasn't been updated with new property requirements. + TCPConnectionString = TCPConnectionString.RemoveAuthAndCredsProperties(); + } + if (TracingEnabled) { TraceListener = new BaseEventListener(); @@ -336,7 +341,7 @@ static DataTestUtility() // globally. SqlAuthenticationProvider.SetProvider( SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, - new ManagedIdentityProvider()); + new UserAssignedManagedIdentityProvider()); } public static IEnumerable ConnectionStrings => GetConnectionStrings(withEnclave: true); @@ -363,42 +368,6 @@ public static IEnumerable GetConnectionStrings(bool withEnclave) } } - private static string GenerateAccessToken(string authorityURL, string aADAuthUserID, string aADAuthPassword) - { - return AcquireTokenAsync(authorityURL, aADAuthUserID, aADAuthPassword).GetAwaiter().GetResult(); - } - - private static Task AcquireTokenAsync(string authorityURL, string userID, string password) => Task.Run(() => - { - // The below properties are set specific to test configurations. - string scope = "https://database.windows.net//.default"; - string applicationName = "Microsoft Data SqlClient Manual Tests"; - string clientVersion = "1.0.0.0"; - string adoClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; - - IPublicClientApplication app = PublicClientApplicationBuilder.Create(adoClientId) - .WithAuthority(authorityURL) - .WithClientName(applicationName) - .WithClientVersion(clientVersion) - .Build(); - - AuthenticationResult result; - string[] scopes = new string[] { scope }; - - // Note: CorrelationId, which existed in ADAL, can not be set in MSAL (yet?). - // parameter.ConnectionId was passed as the CorrelationId in ADAL to aid support in troubleshooting. - // If/When MSAL adds CorrelationId support, it should be passed from parameters here, too. - - SecureString securePassword = new SecureString(); - - securePassword.MakeReadOnly(); - #pragma warning disable CS0618 // Type or member is obsolete - result = app.AcquireTokenByUsernamePassword(scopes, userID, password).ExecuteAsync().Result; - #pragma warning restore CS0618 // Type or member is obsolete - - return result.AccessToken; - }); - public static bool IsKerberosTest => !string.IsNullOrEmpty(KerberosDomainUser) && !string.IsNullOrEmpty(KerberosDomainPassword); public static bool IsNotKerberosTest => !IsKerberosTest; @@ -610,35 +579,23 @@ public static bool AreConnStringSetupForAE() public static bool IsSGXEnclaveConnStringSetup() => !string.IsNullOrEmpty(TCPConnectionStringAASSGX); - public static bool IsAADPasswordConnStrSetup() - { - return !string.IsNullOrEmpty(AADPasswordConnectionString); - } + /// + /// Returns if the provided connection string points to an Azure SQL Server endpoint. + /// Use this instead of a separate AzureSqlConnectionString config property to detect Azure SQL targets. + /// + public static bool IsAzureSqlConnectionString(string connectionString) + => !string.IsNullOrEmpty(connectionString) + && Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(connectionString).DataSource); - public static bool IsAADServicePrincipalSetup() - { - return !string.IsNullOrEmpty(AADServicePrincipalId) && !string.IsNullOrEmpty(AADServicePrincipalSecret); - } + public static bool IsAzureConnStringSetup() => IsTCPConnStringSetup() && IsAzureSqlConnectionString(TCPConnectionString); - public static bool IsAADAuthorityURLSetup() - { - return !string.IsNullOrEmpty(AADAuthorityURL); - } + public static bool IsAADServicePrincipalSetup() => !string.IsNullOrEmpty(AADServicePrincipalId) && !string.IsNullOrEmpty(AADServicePrincipalSecret); - public static bool IsNotAzureServer() - { - return !AreConnStringsSetup() || !Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TCPConnectionString).DataSource); - } + public static bool IsNotAzureServer() => !AreConnStringsSetup() || !Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TCPConnectionString).DataSource); - public static bool IsAzureServer() - { - return AreConnStringsSetup() && Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TCPConnectionString).DataSource); - } + public static bool IsAzureServer() => AreConnStringsSetup() && Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TCPConnectionString).DataSource); - public static bool IsNotNamedInstance() - { - return !AreConnStringsSetup() || !new SqlConnectionStringBuilder(TCPConnectionString).DataSource.Contains(@"\"); - } + public static bool IsNotNamedInstance() => !AreConnStringsSetup() || !new SqlConnectionStringBuilder(TCPConnectionString).DataSource.Contains(@"\"); public static bool IsLocalHost() { @@ -648,21 +605,26 @@ public static bool IsLocalHost() // Synapse: Always Encrypted is not supported with Azure Synapse. // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse - public static bool IsAKVSetupAvailable() - { - return AKVBaseUri != null && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); - } - - private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId }); - - public static TokenCredential GetTokenCredential() - { - return s_defaultCredential; - } + public static bool IsAKVSetupAvailable() => + AKVBaseUri != null + && !string.IsNullOrEmpty(UserManagedIdentityClientId) + && !string.IsNullOrEmpty(AKVTenantId) + && IsNotAzureSynapse(); + + private static readonly DefaultAzureCredential s_defaultCredential = + new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId }); + + public static string GetUserIdentityConnectionString() + => TCPConnectionString + .RemoveAuthAndCredsProperties() + .AddManagedIdentityAuthenticationToConnString() + .AddUserToConnString(UserManagedIdentityClientId); + + public static TokenCredential GetTokenCredential() => s_defaultCredential; public static bool IsTargetReadyForAeWithKeyStore() { - return DataTestUtility.AreConnStringSetupForAE() + return AreConnStringSetupForAE() #if !NETFRAMEWORK // AE tests on Windows will use the Cert Store. On non-Windows, they require AKV. && (OperatingSystem.IsWindows() || DataTestUtility.IsAKVSetupAvailable()) @@ -698,30 +660,24 @@ public static bool IsUTF8Supported() bool retval = false; if (AreConnStringsSetup() && IsNotAzureSynapse()) { - using (SqlConnection connection = new SqlConnection(TCPConnectionString)) - using (SqlCommand command = new SqlCommand()) - { - command.Connection = connection; - command.CommandText = "SELECT CONNECTIONPROPERTY('SUPPORT_UTF8')"; - connection.Open(); + using SqlConnection connection = new(TCPConnectionString); + using SqlCommand command = new(); + command.Connection = connection; + command.CommandText = "SELECT CONNECTIONPROPERTY('SUPPORT_UTF8')"; + connection.Open(); - using (SqlDataReader reader = command.ExecuteReader()) - { - while (reader.Read()) - { - // CONNECTIONPROPERTY('SUPPORT_UTF8') returns NULL in SQLServer versions that don't support UTF-8. - retval = !reader.IsDBNull(0); - } - } + using SqlDataReader reader = command.ExecuteReader(); + while (reader.Read()) + { + // CONNECTIONPROPERTY('SUPPORT_UTF8') returns NULL in SQLServer versions that don't support UTF-8. + retval = !reader.IsDBNull(0); } } return retval; } - public static bool IsTCPConnectionStringPasswordIncluded() - { - return RetrieveValueFromConnStr(TCPConnectionString, new string[] { "Password", "PWD" }) != string.Empty; - } + public static bool IsTCPConnectionStringPasswordIncluded() => + RetrieveValueFromConnStr(TCPConnectionString, ["Password", "PWD"]) != string.Empty; public static bool DoesHostAddressContainBothIPv4AndIPv6() { @@ -729,12 +685,10 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6() { return false; } - using (var connection = new SqlConnection(DNSCachingConnString)) - { - List ipAddresses = Dns.GetHostAddresses(connection.DataSource).ToList(); - return ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetwork) && - ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetworkV6); - } + using var connection = new SqlConnection(DNSCachingConnString); + List ipAddresses = Dns.GetHostAddresses(connection.DataSource).ToList(); + return ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetwork) && + ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetworkV6); } public static string GetShortName(string prefix, bool withBracket = true) => @@ -810,48 +764,42 @@ public static void DropDatabase(SqlConnection sqlConnection, string dbName) public static bool IsLocalDBInstalled() => !string.IsNullOrEmpty(LocalDbAppName?.Trim()) && IsIntegratedSecuritySetup(); public static bool IsLocalDbSharedInstanceSetup() => !string.IsNullOrEmpty(LocalDbSharedInstanceName?.Trim()) && IsIntegratedSecuritySetup(); public static bool IsIntegratedSecuritySetup() => SupportsIntegratedSecurity; + public static async Task IsAccessTokenAsyncSetup() => + !string.IsNullOrEmpty(await GetSystemIdentityAccessTokenAsync()) + || !string.IsNullOrEmpty(await GetUserIdentityAccessTokenAsync()); - public static string GetAccessToken() - { - if (AADAccessToken == null && IsAADPasswordConnStrSetup() && IsAADAuthorityURLSetup()) - { - string username = RetrieveValueFromConnStr(AADPasswordConnectionString, new string[] { "User ID", "UID" }); - string password = RetrieveValueFromConnStr(AADPasswordConnectionString, new string[] { "Password", "PWD" }); - AADAccessToken = GenerateAccessToken(AADAuthorityURL, username, password); - } - // Creates a new Object Reference of Access Token - See GitHub Issue 438 - return AADAccessToken != null ? new string(AADAccessToken.ToCharArray()) : null; - } + public static Task GetAccessTokenAsync() => + IsUserManagedIdentitySupported ? GetUserIdentityAccessTokenAsync() : + (IsSystemManagedIdentitySupported ? GetSystemIdentityAccessTokenAsync() : + Task.FromResult(null)); - public static string GetSystemIdentityAccessToken() + public static async Task GetSystemIdentityAccessTokenAsync() { - if (ManagedIdentitySupported && SupportsSystemAssignedManagedIdentity && AADSystemIdentityAccessToken == null && IsAADPasswordConnStrSetup()) + if (IsSystemManagedIdentitySupported && AADSystemIdentityAccessToken == null && IsAzureConnStringSetup()) { - AADSystemIdentityAccessToken = AADUtility.GetManagedIdentityToken().GetAwaiter().GetResult(); + AADSystemIdentityAccessToken = await AADUtility.GetManagedIdentityToken(); if (AADSystemIdentityAccessToken == null) { - ManagedIdentitySupported = false; + IsSystemManagedIdentitySupported = false; } } return AADSystemIdentityAccessToken != null ? new string(AADSystemIdentityAccessToken.ToCharArray()) : null; } - public static string GetUserIdentityAccessToken() + public static async Task GetUserIdentityAccessTokenAsync() { - if (ManagedIdentitySupported && AADUserIdentityAccessToken == null && IsAADPasswordConnStrSetup()) + if (IsUserManagedIdentitySupported && AADUserIdentityAccessToken == null && IsAzureConnStringSetup()) { // Pass User Assigned Managed Identity Client Id here. - AADUserIdentityAccessToken = AADUtility.GetManagedIdentityToken(UserManagedIdentityClientId).GetAwaiter().GetResult(); + AADUserIdentityAccessToken = await AADUtility.GetManagedIdentityToken(UserManagedIdentityClientId); if (AADUserIdentityAccessToken == null) { - ManagedIdentitySupported = false; + IsUserManagedIdentitySupported = false; } } return AADUserIdentityAccessToken != null ? new string(AADUserIdentityAccessToken.ToCharArray()) : null; } - public static bool IsAccessTokenSetup() => !string.IsNullOrEmpty(GetAccessToken()); - public static bool IsFileStreamSetup() => !string.IsNullOrEmpty(FileStreamDirectory) && IsNotAzureServer() && IsNotAzureSynapse(); private static bool CheckException(Exception ex, string exceptionMessage, bool innerExceptionMustBeNull) where TException : Exception @@ -913,7 +861,7 @@ public static TException AssertThrowsInner( TException ex = AssertThrows(actionThatFails, exceptionMessage); Assert.NotNull(ex.InnerException); - Assert.IsAssignableFrom(ex.InnerException); + Assert.IsType(ex.InnerException, exactMatch: false); if (innerExceptionMessage != null) { @@ -979,19 +927,6 @@ public static TException ExpectFailure(Action actionThatFails, strin } } - public static string GenerateObjectName() - { - return string.Format("TEST_{0}{1}{2}", Environment.GetEnvironmentVariable("ComputerName"), Environment.TickCount, Guid.NewGuid()).Replace('-', '_'); - } - - // Returns randomly generated characters of length 11. - public static string GenerateRandomCharacters(string prefix) - { - string path = Path.GetRandomFileName(); - path = path.Replace(".", ""); // Remove period. - return prefix + path; - } - public static void RunNonQuery(string connectionString, string sql, int numberOfRetries = 0) { int retries = 0; @@ -999,14 +934,12 @@ public static void RunNonQuery(string connectionString, string sql, int numberOf { try { - using (SqlConnection connection = new SqlConnection(connectionString)) - using (SqlCommand command = connection.CreateCommand()) - { - connection.Open(); - command.CommandText = sql; - command.ExecuteNonQuery(); - break; - } + using SqlConnection connection = new(connectionString); + using SqlCommand command = connection.CreateCommand(); + connection.Open(); + command.CommandText = sql; + command.ExecuteNonQuery(); + break; } catch (Exception) { @@ -1088,36 +1021,6 @@ public static string FetchKeyInConnStr(string connStr, string[] keys) return null; } - public static string RemoveKeysInConnStr(string connStr, string[] keysToRemove) - { - // tokenize connection string and remove input keys. - string res = ""; - if (connStr != null && keysToRemove != null) - { - string[] keys = connStr.Split(';'); - foreach (var key in keys) - { - if (!string.IsNullOrEmpty(key.Trim())) - { - bool removeKey = false; - foreach (var keyToRemove in keysToRemove) - { - if (key.Trim().ToLower().StartsWith(keyToRemove.Trim().ToLower(), StringComparison.Ordinal)) - { - removeKey = true; - break; - } - } - if (!removeKey) - { - res += key + ";"; - } - } - } - } - return res; - } - public static string RetrieveValueFromConnStr(string connStr, string[] keywords) { // tokenize connection string and retrieve value for a specific key. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UserAssignedManagedIdentityProvider.cs similarity index 97% rename from src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs rename to src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UserAssignedManagedIdentityProvider.cs index baafec4205..bc7cff7b61 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UserAssignedManagedIdentityProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests; -internal class ManagedIdentityProvider : SqlAuthenticationProvider +internal class UserAssignedManagedIdentityProvider : SqlAuthenticationProvider { // Our cache of managed identity user Ids to credential instances. private readonly ConcurrentDictionary diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/XEventScope.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/XEventScope.cs index ab89a97b10..090204bc67 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/XEventScope.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/XEventScope.cs @@ -6,6 +6,7 @@ using System.Data; using System.Threading; using System.Xml; +using Microsoft.Data.SqlClient.Tests.Common; #nullable enable @@ -62,7 +63,7 @@ public XEventScope( string targetSpecification, ushort durationInMinutes = 5) { - SessionName = DataTestUtility.GenerateRandomCharacters(sessionName); + SessionName = TestRandomUtilities.GenerateRandomCharacters(sessionName); _connection = connection; if (connection.State is not ConnectionState.Open) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs index e5616776b1..21dd9953d7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs @@ -20,26 +20,21 @@ public AADFedAuthTokenRefreshTest(ITestOutputHelper testOutputHelper) _testOutputHelper = testOutputHelper; } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureConnStringSetup))] public void FedAuthTokenRefreshTest() { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity); try { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId)); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, new UserAssignedManagedIdentityProvider()); - string connectionString = DataTestUtility.AADPasswordConnectionString; + string connectionString = DataTestUtility.GetUserIdentityConnectionString(); - using SqlConnection connection = new SqlConnection(connectionString); + using SqlConnection connection = new(connectionString); connection.Open(); - string oldTokenHash = ""; - DateTime? oldExpiryDateTime = FedAuthTokenHelper.SetTokenExpiryDateTime(connection, minutesToExpire: 1, out oldTokenHash); + DateTime? oldExpiryDateTime = FedAuthTokenHelper.SetTokenExpiryDateTime(connection, minutesToExpire: 1, out string oldTokenHash); Assert.True(oldExpiryDateTime != null, "Failed to make token expiry to expire in one minute."); // Convert and display the old expiry into local time which should be in 1 minute from now @@ -56,7 +51,7 @@ public void FedAuthTokenRefreshTest() Assert.True(result != string.Empty, "The connection's command must return a value"); // The new connection will use the same FedAuthToken but will refresh it first as it will expire in 1 minute. - using (SqlConnection connection2 = new SqlConnection(connectionString)) + using (SqlConnection connection2 = new(connectionString)) { connection2.Open(); @@ -80,9 +75,7 @@ public void FedAuthTokenRefreshTest() if (original is not null) { // Reset to driver internal provider. - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, original); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index 960f3ee822..4fd2d9ac8b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -249,7 +249,7 @@ public void PrepUnprepTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void SqlVariantTest() { - string tableName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("AdapterTest"); // good test for null values and unicode strings using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand cmd = new SqlCommand(null, conn)) @@ -345,7 +345,7 @@ public void SqlVariantTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void ParameterTest_AllTypes() { - string procName = DataTestUtility.GenerateObjectName(); + string procName = DataTestUtility.GetShortName("AdapterTest"); string spCreateAllTypes = "CREATE PROCEDURE " + procName + " " + "@Cnumeric numeric(10,2) OUTPUT, " + @@ -936,8 +936,8 @@ public void UpdateRefreshTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void UpdateNullTest() { - string tableName = DataTestUtility.GenerateObjectName(); - string procName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("AdapterTest"); + string procName = DataTestUtility.GetShortName("AdapterTest"); string createTable = "CREATE TABLE " + tableName + "(cvarbin VARBINARY(7000), cimage IMAGE)"; string createSP = @@ -993,8 +993,8 @@ public void UpdateNullTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void UpdateOffsetTest() { - string tableName = DataTestUtility.GenerateObjectName(); - string procName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("AdapterTest"); + string procName = DataTestUtility.GetShortName("AdapterTest"); string createTable = "CREATE TABLE " + tableName + "(cvarbin VARBINARY(7000), cimage IMAGE)"; string createSP = diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs index 4b63ff655f..38e55ddb62 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs @@ -14,19 +14,19 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class ConnectionPoolConnectionStringProvider : IEnumerable { - private static readonly string _TCPConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { + private static readonly string s_tcpConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MultipleActiveResultSets = false, Pooling = true}.ConnectionString; - private static readonly string _tcpMarsConnStr = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { + private static readonly string s_tcpMarsConnStr = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MultipleActiveResultSets = true, Pooling = true }.ConnectionString; public IEnumerator GetEnumerator() { - yield return new object[] { _TCPConnectionString }; + yield return new object[] { s_tcpConnectionString }; if (DataTestUtility.IsNotAzureSynapse()) { - yield return new object[] { _tcpMarsConnStr }; + yield return new object[] { s_tcpMarsConnStr }; } } @@ -35,21 +35,21 @@ public IEnumerator GetEnumerator() public class ConnectionPoolConnectionStringAndPoolVersionProvider : IEnumerable { - private static readonly string _TCPConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { + private static readonly string s_tcpConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MultipleActiveResultSets = false, Pooling = true }.ConnectionString; - private static readonly string _tcpMarsConnStr = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { + private static readonly string s_tcpMarsConnStr = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MultipleActiveResultSets = true, Pooling = true }.ConnectionString; public IEnumerator GetEnumerator() { - yield return new object[] { _TCPConnectionString, false }; - yield return new object[] { _TCPConnectionString, true }; + yield return new object[] { s_tcpConnectionString, false }; + yield return new object[] { s_tcpConnectionString, true }; if (DataTestUtility.IsNotAzureSynapse()) { - yield return new object[] { _tcpMarsConnStr, false }; - yield return new object[] { _tcpMarsConnStr, true }; + yield return new object[] { s_tcpMarsConnStr, false }; + yield return new object[] { s_tcpMarsConnStr, true }; } } @@ -72,21 +72,21 @@ public static void BasicConnectionPoolingTest(string connectionString) InternalConnectionWrapper internalConnection; ConnectionPoolWrapper connectionPool; - using (SqlConnection connection = new SqlConnection(connectionString)) + using (SqlConnection connection = new(connectionString)) { connection.Open(); internalConnection = new InternalConnectionWrapper(connection); connectionPool = new ConnectionPoolWrapper(connection); } - using (SqlConnection connection2 = new SqlConnection(connectionString)) + using (SqlConnection connection2 = new(connectionString)) { connection2.Open(); Assert.True(internalConnection.IsInternalConnectionOf(connection2), "New connection does not use same internal connection"); Assert.True(connectionPool.ContainsConnection(connection2), "New connection is in a different pool"); } - using (SqlConnection connection3 = new SqlConnection(connectionString + ";App=SqlConnectionPoolUnitTest;")) + using (SqlConnection connection3 = new(connectionString + ";App=SqlConnectionPoolUnitTest;")) { connection3.Open(); Assert.False(internalConnection.IsInternalConnectionOf(connection3), "Connection with different connection string uses same internal connection"); @@ -95,7 +95,7 @@ public static void BasicConnectionPoolingTest(string connectionString) connectionPool.Cleanup(); - using (SqlConnection connection4 = new SqlConnection(connectionString)) + using (SqlConnection connection4 = new(connectionString)) { connection4.Open(); Assert.True(internalConnection.IsInternalConnectionOf(connection4), "New connection does not use same internal connection"); @@ -103,31 +103,31 @@ public static void BasicConnectionPoolingTest(string connectionString) } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup), nameof(DataTestUtility.IsAADAuthorityURLSetup))] - public static void AccessTokenConnectionPoolingTest() + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureConnStringSetup))] + public static async Task AccessTokenConnectionPoolingTest() { SqlConnection.ClearAllPools(); - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD", "Authentication" }; - string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - - using SqlConnection connection = new SqlConnection(connectionString); - connection.AccessToken = DataTestUtility.GetAccessToken(); + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + connection.AccessToken = await DataTestUtility.GetAccessTokenAsync(); connection.Open(); - InternalConnectionWrapper internalConnection = new InternalConnectionWrapper(connection); - ConnectionPoolWrapper connectionPool = new ConnectionPoolWrapper(connection); + InternalConnectionWrapper internalConnection = new(connection); + ConnectionPoolWrapper connectionPool = new(connection); connection.Close(); - using SqlConnection connection2 = new SqlConnection(connectionString); - connection2.AccessToken = DataTestUtility.GetAccessToken(); + using SqlConnection connection2 = new(DataTestUtility.TCPConnectionString); + connection2.AccessToken = await DataTestUtility.GetAccessTokenAsync(); connection2.Open(); Assert.True(internalConnection.IsInternalConnectionOf(connection2), "New connection does not use same internal connection"); Assert.True(connectionPool.ContainsConnection(connection2), "New connection is in a different pool"); connection2.Close(); - using SqlConnection connection3 = new SqlConnection(connectionString + ";App=SqlConnectionPoolUnitTest;"); - connection3.AccessToken = DataTestUtility.GetAccessToken(); + string azureConnStrWithApp = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + ApplicationName = "SqlConnectionPoolUnitTest" + }.ConnectionString; + using SqlConnection connection3 = new(azureConnStrWithApp); + connection3.AccessToken = await DataTestUtility.GetAccessTokenAsync(); connection3.Open(); Assert.False(internalConnection.IsInternalConnectionOf(connection3), "Connection with different connection string uses same internal connection"); Assert.False(connectionPool.ContainsConnection(connection3), "Connection with different connection string uses same connection pool"); @@ -135,8 +135,8 @@ public static void AccessTokenConnectionPoolingTest() connectionPool.Cleanup(); - using SqlConnection connection4 = new SqlConnection(connectionString); - connection4.AccessToken = DataTestUtility.GetAccessToken(); + using SqlConnection connection4 = new(DataTestUtility.TCPConnectionString); + connection4.AccessToken = await DataTestUtility.GetAccessTokenAsync(); connection4.Open(); Assert.True(internalConnection.IsInternalConnectionOf(connection4), "New connection does not use same internal connection"); Assert.True(connectionPool.ContainsConnection(connection4), "New connection is in a different pool"); @@ -156,21 +156,19 @@ public static void ClearAllPoolsTest(string connectionString, bool usePoolV2) SqlConnection.ClearAllPools(); Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); - using (SqlConnection connection = new SqlConnection(connectionString)) - { - connection.Open(); - ConnectionPoolWrapper pool = new ConnectionPoolWrapper(connection); - connection.Close(); - ConnectionPoolWrapper[] allPools = ConnectionPoolWrapper.AllConnectionPools(); + using SqlConnection connection = new(connectionString); + connection.Open(); + ConnectionPoolWrapper pool = new(connection); + connection.Close(); + ConnectionPoolWrapper[] allPools = ConnectionPoolWrapper.AllConnectionPools(); - DataTestUtility.AssertEqualsWithDescription(1, allPools.Length, "Incorrect number of pools exist."); - Assert.True(allPools[0].Equals(pool), "Saved pool is not in the list of all pools"); - DataTestUtility.AssertEqualsWithDescription(1, pool.ConnectionCount, "Saved pool has incorrect number of connections"); + DataTestUtility.AssertEqualsWithDescription(1, allPools.Length, "Incorrect number of pools exist."); + Assert.True(allPools[0].Equals(pool), "Saved pool is not in the list of all pools"); + DataTestUtility.AssertEqualsWithDescription(1, pool.ConnectionCount, "Saved pool has incorrect number of connections"); - SqlConnection.ClearAllPools(); - Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); - DataTestUtility.AssertEqualsWithDescription(0, pool.ConnectionCount, "Saved pool has incorrect number of connections."); - } + SqlConnection.ClearAllPools(); + Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); + DataTestUtility.AssertEqualsWithDescription(0, pool.ConnectionCount, "Saved pool has incorrect number of connections."); } /// @@ -181,7 +179,7 @@ public static void ClearAllPoolsTest(string connectionString, bool usePoolV2) [ClassData(typeof(ConnectionPoolConnectionStringProvider))] public static void ReclaimEmancipatedOnOpenTest(string connectionString) { - string newConnectionString = (new SqlConnectionStringBuilder(connectionString) { MaxPoolSize = 1 }).ConnectionString; + string newConnectionString = new SqlConnectionStringBuilder(connectionString) { MaxPoolSize = 1 }.ConnectionString; SqlConnection.ClearAllPools(); InternalConnectionWrapper internalConnection = CreateEmancipatedConnection(newConnectionString); @@ -193,12 +191,10 @@ public static void ReclaimEmancipatedOnOpenTest(string connectionString) DataTestUtility.AssertEqualsWithDescription(1, connectionPool.ConnectionCount, "Wrong number of connections in the pool."); DataTestUtility.AssertEqualsWithDescription(0, connectionPool.FreeConnectionCount, "Wrong number of free connections in the pool."); - using (SqlConnection connection = new SqlConnection(newConnectionString)) - { - connection.Open(); - Assert.True(internalConnection.IsInternalConnectionOf(connection), "Connection has wrong internal connection"); - Assert.True(connectionPool.ContainsConnection(connection), "Connection is in wrong connection pool"); - } + using SqlConnection connection = new(newConnectionString); + connection.Open(); + Assert.True(internalConnection.IsInternalConnectionOf(connection), "Connection has wrong internal connection"); + Assert.True(connectionPool.ContainsConnection(connection), "Connection is in wrong connection pool"); } /// @@ -208,15 +204,15 @@ public static void ReclaimEmancipatedOnOpenTest(string connectionString) [ClassData(typeof(ConnectionPoolConnectionStringProvider))] public static void MaxPoolWaitForConnectionTest(string connectionString) { - string newConnectionString = (new SqlConnectionStringBuilder(connectionString) { MaxPoolSize = 1 }).ConnectionString; + string newConnectionString = new SqlConnectionStringBuilder(connectionString) { MaxPoolSize = 1 }.ConnectionString; SqlConnection.ClearAllPools(); - using SqlConnection connection1 = new SqlConnection(newConnectionString); + using SqlConnection connection1 = new(newConnectionString); connection1.Open(); - InternalConnectionWrapper internalConnection = new InternalConnectionWrapper(connection1); - ConnectionPoolWrapper connectionPool = new ConnectionPoolWrapper(connection1); - ManualResetEventSlim taskAllowedToSpeak = new ManualResetEventSlim(false); + InternalConnectionWrapper internalConnection = new(connection1); + ConnectionPoolWrapper connectionPool = new(connection1); + using ManualResetEventSlim taskAllowedToSpeak = new(false); Task waitTask = Task.Factory.StartNew(() => MaxPoolWaitForConnectionTask(newConnectionString, internalConnection, connectionPool, taskAllowedToSpeak)); int count = 5; @@ -236,7 +232,7 @@ internal static InternalConnectionWrapper ReplacementConnectionUsesSemaphoreTask { InternalConnectionWrapper internalConnection = null; - using (SqlConnection connection = new SqlConnection(connectionString)) + using (SqlConnection connection = new(connectionString)) { try { @@ -257,14 +253,14 @@ internal static InternalConnectionWrapper ReplacementConnectionUsesSemaphoreTask private static InternalConnectionWrapper CreateEmancipatedConnection(string connectionString) { - SqlConnection connection = new SqlConnection(connectionString); + SqlConnection connection = new(connectionString); connection.Open(); return new InternalConnectionWrapper(connection); } private static void MaxPoolWaitForConnectionTask(string connectionString, InternalConnectionWrapper internalConnection, ConnectionPoolWrapper connectionPool, ManualResetEventSlim waitToSpeak) { - using SqlConnection connection = new SqlConnection(connectionString); + using SqlConnection connection = new(connectionString); connection.Open(); waitToSpeak.Wait(); Assert.True(internalConnection.IsInternalConnectionOf(connection), "Connection has wrong internal connection"); @@ -273,7 +269,7 @@ private static void MaxPoolWaitForConnectionTask(string connectionString, Intern internal static SqlConnection ReplacementConnectionObeys0TimeoutTask(string connectionString) { - SqlConnection connection = new SqlConnection(connectionString); + SqlConnection connection = new(connectionString); connection.Open(); return connection; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index b60b4a1e9e..2916bbf905 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -7,6 +7,7 @@ using System.Security; using System.Threading.Tasks; using Azure.Core; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -16,132 +17,107 @@ public class AADConnectionTest { private static void ConnectAndDisconnect(string connectionString, SqlCredential credential = null) { - using (SqlConnection conn = new SqlConnection(connectionString)) + using SqlConnection conn = new(connectionString); + if (credential != null) { - if (credential != null) - { - conn.Credential = credential; - } - conn.Open(); - - Assert.True(conn.State == System.Data.ConnectionState.Open); + conn.Credential = credential; } + conn.Open(); + + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } private static bool AreConnStringsSetup() => DataTestUtility.AreConnStringsSetup(); private static bool IsAzure() => !DataTestUtility.IsNotAzureServer(); - private static bool IsAccessTokenSetup() => DataTestUtility.IsAccessTokenSetup(); - private static bool IsAADConnStringsSetup() => DataTestUtility.IsAADPasswordConnStrSetup(); - private static bool IsManagedIdentitySetup() => DataTestUtility.ManagedIdentitySupported; - private static bool SupportsSystemAssignedManagedIdentity() => DataTestUtility.SupportsSystemAssignedManagedIdentity; + private static bool IsAccessTokenSetup() => DataTestUtility.IsAccessTokenAsyncSetup().GetAwaiter().GetResult(); + private static bool IsAzureSqlConnStringSetup() => DataTestUtility.IsAzureConnStringSetup(); + private static bool IsManagedIdentitySetup() => DataTestUtility.IsUserManagedIdentitySupported; + private static bool SupportsSystemAssignedManagedIdentity() => DataTestUtility.IsSystemManagedIdentitySupported; - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] - public static void AccessTokenTest() + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] + public static async Task AccessTokenTest() { - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD", "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - - using (SqlConnection connection = new SqlConnection(connStr)) - { - connection.AccessToken = DataTestUtility.GetAccessToken(); - connection.Open(); + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + connection.AccessToken = await DataTestUtility.GetAccessTokenAsync(); + await connection.OpenAsync(); - Assert.True(connection.State == System.Data.ConnectionState.Open); - } + Assert.Equal(System.Data.ConnectionState.Open, connection.State); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] - public static void InvalidAccessTokenTest() + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] + public static async Task InvalidAccessTokenTest() { - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD", "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - - using (SqlConnection connection = new SqlConnection(connStr)) - { - connection.AccessToken = DataTestUtility.GetAccessToken() + "abc"; - SqlException e = Assert.Throws(() => connection.Open()); + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + connection.AccessToken = await DataTestUtility.GetAccessTokenAsync() + "abc"; + SqlException e = Assert.Throws(() => connection.Open()); - string expectedMessage = "Login failed for user"; - Assert.Contains(expectedMessage, e.Message); - } + string expectedMessage = "Login failed for user"; + Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] - public static void AccessTokenWithAuthType() + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] + public static async Task AccessTokenWithAuthType() { - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - - using (SqlConnection connection = new SqlConnection(connStr)) - { - InvalidOperationException e = Assert.Throws(() => - connection.AccessToken = DataTestUtility.GetAccessToken()); - - string expectedMessage = "Cannot set the AccessToken property if 'Authentication' has been specified in the connection string."; - Assert.Contains(expectedMessage, e.Message); - } + using SqlConnection connection = new(DataTestUtility.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString()); + InvalidOperationException e = await Assert.ThrowsAsync + (async () => + connection.AccessToken = await DataTestUtility.GetAccessTokenAsync() + ); + + string expectedMessage = "Cannot set the AccessToken property if 'Authentication' has been specified in the connection string."; + Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] - public static void AccessTokenWithCred() + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] + public static async Task AccessTokenWithCred() { - // Remove cred info and add invalid token - string[] credKeys = { "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); + string connString = DataTestUtility.TCPConnectionString + .AddUserToConnString() + .AddPasswordToConnString(); - using (SqlConnection connection = new SqlConnection(connStr)) - { - InvalidOperationException e = Assert.Throws(() => - connection.AccessToken = DataTestUtility.GetAccessToken()); + using SqlConnection connection = new(connString); + InvalidOperationException e = await Assert.ThrowsAsync + (async () => + connection.AccessToken = await DataTestUtility.GetAccessTokenAsync() + ); - string expectedMessage = "Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string."; - Assert.Contains(expectedMessage, e.Message); - } + string expectedMessage = "Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string."; + Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] public static void AccessTokenTestWithEmptyToken() { - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD", "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); + string connStr = DataTestUtility.TCPConnectionString; - using (SqlConnection connection = new SqlConnection(connStr)) - { - connection.AccessToken = ""; - SqlException e = Assert.Throws(() => connection.Open()); + using SqlConnection connection = new(connStr); + connection.AccessToken = ""; + SqlException e = Assert.Throws(() => connection.Open()); - string expectedMessage = "A connection was successfully established with the server, but then an error occurred during the login process."; - Assert.Contains(expectedMessage, e.Message); - } + string expectedMessage = "A connection was successfully established with the server, but then an error occurred during the login process."; + Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] public static void AccessTokenTestWithIntegratedSecurityTrue() { - // Remove cred info and add invalid token - string[] credKeys = { "User ID", "Password", "UID", "PWD", "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + "Integrated Security=True;"; + string connStr = DataTestUtility.TCPConnectionString + .AddIntegratedSecurityToConnString(); - using (SqlConnection connection = new SqlConnection(connStr)) - { - InvalidOperationException e = Assert.Throws(() => connection.AccessToken = ""); + using SqlConnection connection = new(connStr); + InvalidOperationException e = Assert.Throws(() => connection.AccessToken = ""); - string expectedMessage = "Cannot set the AccessToken property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'."; - Assert.Contains(expectedMessage, e.Message); - } + string expectedMessage = "Cannot set the AccessToken property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'."; + Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAzureSqlConnStringSetup))] public static void InvalidAuthTypeTest() { - // Remove cred info and add invalid token - string[] credKeys = { "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + "Authentication=Active Directory Pass;"; + string connStr = DataTestUtility.TCPConnectionString + .AddInvalidAADAuthenticationToConnString(); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStr)); @@ -149,10 +125,14 @@ public static void InvalidAuthTypeTest() Assert.Contains(expectedMessage, e.Message, StringComparison.OrdinalIgnoreCase); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void AADPasswordWithIntegratedSecurityTrue() { - string connStr = DataTestUtility.AADPasswordConnectionString + "Integrated Security=True;"; + string connStr = DataTestUtility.TCPConnectionString + .AddAADPasswordAuthenticationToConnString() + .AddUserToConnString() + .AddPasswordToConnString() + .AddIntegratedSecurityToConnString(); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStr)); @@ -160,93 +140,55 @@ public static void AADPasswordWithIntegratedSecurityTrue() Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void GetAccessTokenByPasswordTest() + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] + public static void TestCustomProviderAuthentication() { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity); try { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId)); - #pragma warning restore 0618 // Type or member is obsolete - - using (SqlConnection connection = new SqlConnection(DataTestUtility.AADPasswordConnectionString)) - { - connection.Open(); - Assert.True(connection.State == System.Data.ConnectionState.Open); - } - } - finally - { - if (original is not null) - { - // Reset to driver internal provider. - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original); - #pragma warning restore 0618 // Type or member is obsolete - } - } - } + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, new UserAssignedManagedIdentityProvider()); - [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void TestCustomProviderAuthentication() - { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword); - #pragma warning restore 0618 // Type or member is obsolete + string connStr = DataTestUtility.GetUserIdentityConnectionString(); + // Connect to Azure DB with managed identity and retrieve user name using custom authentication provider + using SqlConnection conn = new(connStr); - try - { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId)); - #pragma warning restore 0618 // Type or member is obsolete - // Connect to Azure DB with password and retrieve user name using custom authentication provider - using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString)) - { - conn.Open(); - using (SqlCommand sqlCommand = new SqlCommand - ( - cmdText: $"SELECT SUSER_SNAME();", - connection: conn, - transaction: null - )) - { - string customerId = (string)sqlCommand.ExecuteScalar(); - string expected = DataTestUtility.RetrieveValueFromConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User ID", "UID" }); - Assert.Equal(expected, customerId); - } - } + conn.Open(); + using SqlCommand sqlCommand = new( + cmdText: "SELECT SUSER_SNAME();", + connection: conn, + transaction: null); + string customerId = (string)sqlCommand.ExecuteScalar(); + // SUSER_SNAME() may return "clientId@tenantId" for managed identity principals. + string clientIdPart = customerId.Contains("@") ? customerId.Substring(0, customerId.IndexOf('@')) : customerId; + Assert.Equal(DataTestUtility.UserManagedIdentityClientId, clientIdPart); } finally { if (original is not null) { // Reset to driver internal provider. - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, original); } } } - [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void ActiveDirectoryPasswordWithNoAuthType() + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] + public static void SqlCredentialsWithNoAuthType() { - // connection fails with expected error message. - string[] AuthKey = { "Authentication" }; - string connStrWithNoAuthType = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, AuthKey); + string connStrWithNoAuthType = DataTestUtility.TCPConnectionString + .AddUserToConnString() + .AddPasswordToConnString(); Assert.Throws(() => ConnectAndDisconnect(connStrWithNoAuthType)); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void IntegratedAuthWithCred() + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] + public static void AADIntegratedAuthWithCred() { - // connection fails with expected error message. - string[] AuthKey = { "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, AuthKey) + "Authentication=Active Directory Integrated;"; + string connStr = DataTestUtility.TCPConnectionString + .AddAADIntegratedAuthenticationToConnString() + .AddUserToConnString() + .AddPasswordToConnString(); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStr)); string[] expectedMessage = { "Cannot use 'Authentication=Active Directory Integrated' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords.", //netfx @@ -254,101 +196,85 @@ public static void IntegratedAuthWithCred() Assert.Contains(e.Message, expectedMessage); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void MFAAuthWithPassword() { // connection fails with expected error message. - string[] AuthKey = { "Authentication" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, AuthKey) + "Authentication=Active Directory Interactive;"; + string connStr = DataTestUtility.TCPConnectionString + .AddAADInteractiveAuthenticationToConnString() + .AddUserToConnString() + .AddPasswordToConnString(); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStr)); string expectedMessage = "Cannot use 'Authentication=Active Directory Interactive' with 'Password' or 'PWD' connection string keywords."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryDeviceCodeFlowWithUserIdMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithUID = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Device Code Flow; UID=someuser;"; + string connStrWithUID = DataTestUtility.TCPConnectionString + .AddAADDeviceCodeFlowAuthenticationToConnString() + .AddUserToConnString("someuser"); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithUID)); string expectedMessage = "Cannot use 'Authentication=Active Directory Device Code Flow' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryDeviceCodeFlowWithCredentialsMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Device Code Flow;"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADDeviceCodeFlowAuthenticationToConnString(); - SecureString str = new SecureString(); - foreach (char c in "hello") - { - str.AppendChar(c); - } - str.MakeReadOnly(); - SqlCredential credential = new SqlCredential("someuser", str); + using SecureString str = TestRandomUtilities.GenerateRandomSecureString(10); + SqlCredential credential = new("someuser", str); InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Device Code Flow' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryManagedIdentityWithCredentialsMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Managed Identity;"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString(); - SecureString str = new SecureString(); - foreach (char c in "hello") - { - str.AppendChar(c); - } - str.MakeReadOnly(); - SqlCredential credential = new SqlCredential("someuser", str); + using SecureString str = TestRandomUtilities.GenerateRandomSecureString(10); + SqlCredential credential = new("someuser", str); InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Managed Identity' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryWorkloadIdentityWithCredentialsMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Workload Identity;"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADWorkloadIdentityAuthenticationToConnString(); - SecureString str = new SecureString(); - foreach (char c in "hello") - { - str.AppendChar(c); - } - str.MakeReadOnly(); - SqlCredential credential = new SqlCredential("someuser", str); + using SecureString str = TestRandomUtilities.GenerateRandomSecureString(10); + SqlCredential credential = new("someuser", str); InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Workload Identity' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup), nameof(IsManagedIdentitySetup))] public static void ActiveDirectoryManagedIdentityWithPasswordMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Managed Identity; Password=anything"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddManagedIdentityAuthenticationToConnString() + .AddPasswordToConnString("anything"); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); @@ -356,34 +282,29 @@ public static void ActiveDirectoryManagedIdentityWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryMSIWithCredentialsMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory MSI;"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADMSIAuthenticationToConnString(); - SecureString str = new SecureString(); - foreach (char c in "hello") - { - str.AppendChar(c); - } - str.MakeReadOnly(); - SqlCredential credential = new SqlCredential("someuser", str); - InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); + using SecureString str = TestRandomUtilities.GenerateRandomSecureString(10); + SqlCredential credential = new("someuser", str); + InvalidOperationException e = Assert.Throws + (() => ConnectAndDisconnect(connStrWithNoCred, credential)); string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory MSI' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryMSIWithPasswordMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=ActiveDirectoryMSI; Password=anything"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADMSIAuthenticationToConnString() + .AddPasswordToConnString(); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); @@ -391,34 +312,30 @@ public static void ActiveDirectoryMSIWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryDefaultWithCredentialsMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=Active Directory Default;"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADDefaultAuthenticationToConnString(); - SecureString str = new SecureString(); - foreach (char c in "hello") - { - str.AppendChar(c); - } - str.MakeReadOnly(); - SqlCredential credential = new SqlCredential("someuser", str); - InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); + using SecureString str = TestRandomUtilities.GenerateRandomSecureString(10); + + SqlCredential credential = new("someuser", str); + InvalidOperationException e = Assert.Throws + (() => ConnectAndDisconnect(connStrWithNoCred, credential)); string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Default' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryDefaultWithPasswordMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=ActiveDirectoryDefault; Password=anything"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADDefaultAuthenticationToConnString() + .AddPasswordToConnString("anything"); ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); @@ -426,117 +343,109 @@ public static void ActiveDirectoryDefaultWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() { // connection fails with expected error message. - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=ActiveDirectoryDefault"; + string connStrWithNoCred = DataTestUtility.TCPConnectionString + .AddAADDefaultAuthenticationToConnString(); + InvalidOperationException e = Assert.Throws(() => { - using (SqlConnection conn = new SqlConnection(connStrWithNoCred)) - { - conn.AccessTokenCallback = (ctx, token) => - Task.FromResult(new SqlAuthenticationToken("my token", DateTimeOffset.MaxValue)); - conn.Open(); + using SqlConnection conn = new(connStrWithNoCred); + conn.AccessTokenCallback = (ctx, token) => + Task.FromResult(new SqlAuthenticationToken("my token", DateTimeOffset.MaxValue)); + conn.Open(); - Assert.NotEqual(System.Data.ConnectionState.Open, conn.State); - } + Assert.NotEqual(System.Data.ConnectionState.Open, conn.State); }); string expectedMessage = "Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string."; Assert.Contains(expectedMessage, e.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void AccessTokenCallbackMustOpenPassAndChangePropertyFail() { - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - var cred = DataTestUtility.GetTokenCredential(); + string connStr = DataTestUtility.TCPConnectionString; + + TokenCredential cred = DataTestUtility.GetTokenCredential(); const string defaultScopeSuffix = "/.default"; - using (SqlConnection conn = new SqlConnection(connStr)) - { - conn.AccessTokenCallback = (ctx, cancellationToken) => - { - string scope = ctx.Resource.EndsWith(defaultScopeSuffix) ? ctx.Resource : ctx.Resource + defaultScopeSuffix; - AccessToken token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); - return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); - }; - conn.Open(); - Assert.Equal(System.Data.ConnectionState.Open, conn.State); - InvalidOperationException ex = Assert.Throws(() => conn.AccessTokenCallback = null); - string expectedMessage = "Not allowed to change the 'AccessTokenCallback' property. The connection's current state is open."; - Assert.Contains(expectedMessage, ex.Message); - } + using SqlConnection conn = new(connStr); + conn.AccessTokenCallback = (ctx, cancellationToken) => + { + string scope = ctx.Resource.EndsWith(defaultScopeSuffix) ? ctx.Resource : ctx.Resource + defaultScopeSuffix; + AccessToken token = cred.GetToken(new TokenRequestContext([scope]), cancellationToken); + return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); + }; + conn.Open(); + Assert.Equal(System.Data.ConnectionState.Open, conn.State); + + InvalidOperationException ex = Assert.Throws(() => conn.AccessTokenCallback = null); + string expectedMessage = "Not allowed to change the 'AccessTokenCallback' property. The connection's current state is open."; + Assert.Contains(expectedMessage, ex.Message); } - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void AccessTokenCallbackReceivesUsernameAndPassword() { var userId = "someuser"; var pwd = "somepassword"; - string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - $"User ID={userId}; Password={pwd}"; - var cred = DataTestUtility.GetTokenCredential(); + string connStr = DataTestUtility.TCPConnectionString + .AddUserToConnString(userId) + .AddPasswordToConnString(pwd); + + TokenCredential cred = DataTestUtility.GetTokenCredential(); const string defaultScopeSuffix = "/.default"; - using (SqlConnection conn = new SqlConnection(connStr)) + + using SqlConnection conn = new(connStr); + conn.AccessTokenCallback = (parms, cancellationToken) => { - conn.AccessTokenCallback = (parms, cancellationToken) => - { - Assert.Equal(userId, parms.UserId); - Assert.Equal(pwd, parms.Password); - string scope = parms.Resource.EndsWith(defaultScopeSuffix) ? parms.Resource : parms.Resource + defaultScopeSuffix; - AccessToken token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); - return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); - }; - conn.Open(); - } + Assert.Equal(userId, parms.UserId); + Assert.Equal(pwd, parms.Password); + string scope = parms.Resource.EndsWith(defaultScopeSuffix) ? parms.Resource : parms.Resource + defaultScopeSuffix; + AccessToken token = cred.GetToken(new TokenRequestContext([scope]), cancellationToken); + return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); + }; + conn.Open(); } - // Test passes locally everytime, but in pieplines fails randomly with uncertainity. + // Test passes locally every time, but in pipelines fails randomly with uncertainty. // e.g. Second Entra ID connection too slow (802ms)! (More than 30% of the first (576ms).) [ActiveIssue("16058")] - [ConditionalFact(nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAzureSqlConnStringSetup))] public static void ConnectionSpeed() { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity); try { - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId)); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, new UserAssignedManagedIdentityProvider()); - var connString = DataTestUtility.AADPasswordConnectionString; + string connString = DataTestUtility.GetUserIdentityConnectionString(); - //Ensure server endpoints are warm - using (var connectionDrill = new SqlConnection(connString)) + // Ensure server endpoints are warm + using (SqlConnection connectionDrill = new(connString)) { connectionDrill.Open(); } SqlConnection.ClearAllPools(); - Stopwatch firstConnectionTime = new Stopwatch(); - Stopwatch secondConnectionTime = new Stopwatch(); + Stopwatch firstConnectionTime = new(); + Stopwatch secondConnectionTime = new(); - using (var connectionDrill = new SqlConnection(connString)) + using (SqlConnection connectionDrill = new(connString)) { firstConnectionTime.Start(); connectionDrill.Open(); firstConnectionTime.Stop(); - using (var connectionDrill2 = new SqlConnection(connString)) - { - secondConnectionTime.Start(); - connectionDrill2.Open(); - secondConnectionTime.Stop(); - } + + using SqlConnection connectionDrill2 = new(connString); + secondConnectionTime.Start(); + connectionDrill2.Open(); + secondConnectionTime.Stop(); } // Subsequent Entra ID connections within a short timeframe should use an auth token cached from the connection pool @@ -548,69 +457,57 @@ public static void ConnectionSpeed() if (original is not null) { // Reset to driver internal provider. - #pragma warning disable 0618 // Type or member is obsolete - SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original); - #pragma warning restore 0618 // Type or member is obsolete + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, original); } } } #region Managed Identity Authentication tests - [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] - public static void AccessToken_SystemManagedIdentityTest() + [ConditionalFact(nameof(IsAzureSqlConnStringSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] + public static async Task AccessToken_SystemManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.AccessToken = DataTestUtility.GetSystemIdentityAccessToken(); - conn.Open(); + using SqlConnection conn = new(DataTestUtility.TCPConnectionString); + conn.AccessToken = await DataTestUtility.GetSystemIdentityAccessTokenAsync(); + conn.Open(); - Assert.True(conn.State == System.Data.ConnectionState.Open); - } + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } - [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))] - public static void AccessToken_UserManagedIdentityTest() + [ConditionalFact(nameof(IsAzureSqlConnStringSetup), nameof(IsManagedIdentitySetup))] + public static async Task AccessToken_UserManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; - string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.AccessToken = DataTestUtility.GetUserIdentityAccessToken(); - conn.Open(); + using SqlConnection conn = new(DataTestUtility.TCPConnectionString); + conn.AccessToken = await DataTestUtility.GetUserIdentityAccessTokenAsync(); + conn.Open(); - Assert.True(conn.State == System.Data.ConnectionState.Open); - } + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] - public static void Azure_AccessToken_SystemManagedIdentityTest() + public static async Task Azure_AccessToken_SystemManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; - string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.AccessToken = DataTestUtility.GetSystemIdentityAccessToken(); - conn.Open(); + string connectionString = DataTestUtility.TCPConnectionString + .RemoveAuthAndCredsProperties(); - Assert.True(conn.State == System.Data.ConnectionState.Open); - } + using SqlConnection conn = new(connectionString); + conn.AccessToken = await DataTestUtility.GetSystemIdentityAccessTokenAsync(); + conn.Open(); + + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup), nameof(IsManagedIdentitySetup))] - public static void Azure_AccessToken_UserManagedIdentityTest() + public static async Task Azure_AccessToken_UserManagedIdentityTest() { - string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; - string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys); - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.AccessToken = DataTestUtility.GetUserIdentityAccessToken(); - conn.Open(); - - Assert.True(conn.State == System.Data.ConnectionState.Open); - } + string connectionString = DataTestUtility.TCPConnectionString + .RemoveAuthAndCredsProperties(); + + using SqlConnection conn = new(connectionString); + conn.AccessToken = await DataTestUtility.GetUserIdentityAccessTokenAsync(); + conn.Open(); + + Assert.Equal(System.Data.ConnectionState.Open, conn.State); } #endregion } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index fbbee8db26..be1d835616 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -19,7 +19,7 @@ public static class ConnectivityTest private const string COL_PROGRAM_NAME = "ProgramName"; private const string COL_HOSTNAME = "HostName"; private static readonly string s_databaseName = "d_" + Guid.NewGuid().ToString().Replace('-', '_'); - private static readonly string s_tableName = DataTestUtility.GenerateObjectName(); + private static readonly string s_tableName = DataTestUtility.GetShortName("ConnectivityTest"); private static readonly string s_connectionString = DataTestUtility.TCPConnectionString; private static readonly string s_dbConnectionString = new SqlConnectionStringBuilder(s_connectionString) { InitialCatalog = s_databaseName }.ConnectionString; private static readonly string s_createDatabaseCmd = $"CREATE DATABASE {s_databaseName}"; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 2a2132dbcb..f07c3f4166 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -71,7 +71,7 @@ public static void MultiQuerySchema() public static void CheckSparseColumnBit() { const int sparseColumns = 4095; - string tempTableName = DataTestUtility.GenerateObjectName(); + string tempTableName = DataTestUtility.GetShortName("DataReaderTest"); // TSQL for "CREATE TABLE" with sparse columns // table name will be provided as an argument @@ -241,7 +241,7 @@ public static void CheckHiddenColumns() // requested. The additional key information is provided as hidden columns and can be seen using // the difference between VisibleFieldCount and FieldCount on the reader - string tempTableName = DataTestUtility.GenerateObjectName(); + string tempTableName = DataTestUtility.GetShortName("DataReaderTest", withBracket: false); string createQuery = $@" create table [{tempTableName}] ( @@ -554,7 +554,7 @@ integrated into a comprehensive development "; - string tableName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("DataReaderTest", withBracket: false); SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); builder.PersistSecurityInfo = true; @@ -696,7 +696,7 @@ public static async Task CanReadBinaryData() byte[] data = Enumerable.Range(0, Size) .Select(i => (byte)(i % 256)) .ToArray(); - string tableName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("DataReaderTest", withBracket: false); using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) { @@ -855,7 +855,7 @@ public static async Task CanReadLargeNTextColumn() { await cn.OpenAsync(); - string tableName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("DataReaderTest", withBracket: false); try { @@ -1063,7 +1063,7 @@ public static async Task CanReadAwkwardDataLengths() { await cn.OpenAsync(); - string tableName = DataTestUtility.GenerateObjectName(); + string tableName = DataTestUtility.GetShortName("DataReaderTest", withBracket: false); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index 81dbd0e35d..23f2aea1e0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -8,6 +8,7 @@ using System.Data; using System.Globalization; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -212,17 +213,17 @@ public static void ExceptionTests() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void VariousExceptionTests() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); // Strip the password in connection string if Authentication=Active Directory Managed Identity as it can not be used with a Password if (builder.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity) { - string[] removeKeys = { "Password", "PWD" }; - string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys); + string[] removeKeys = ["Password", "PWD"]; + string connStr = DataTestUtility.TCPConnectionString.RemoveKeysInConnStr(removeKeys); builder = new SqlConnectionStringBuilder(connStr); } // Test 1 - A - SqlConnectionStringBuilder badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { DataSource = badServer, ConnectTimeout = 1 }; + SqlConnectionStringBuilder badBuilder = new(builder.ConnectionString) { DataSource = badServer, ConnectTimeout = 1 }; using (var sqlConnection = new SqlConnection(badBuilder.ConnectionString)) { using (SqlCommand command = sqlConnection.CreateCommand()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs index 437e02c579..4bbbb3e5de 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -115,7 +116,7 @@ private static void PortNumberInSPNTest(string connectionString, int expectedPor if (DataTestUtility.IsIntegratedSecuritySetup()) { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection" }; - connectionString = DataTestUtility.RemoveKeysInConnStr(connectionString, removeKeys) + $"Integrated Security=true"; + connectionString = connectionString.RemoveKeysInConnStr(removeKeys) + $"Integrated Security=true"; } SqlConnectionStringBuilder builder = new(connectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index a067ad841a..b2bea1efa2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -648,7 +648,7 @@ public static async Task MarsScenarioClientJoin() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void MarsConcurrencyTest() { - var table = DataTestUtility.GenerateObjectName(); + var table = DataTestUtility.GetShortName("MarsConcurrencyTest", withBracket: false); using (var conn = new SqlConnection(DataTestUtility.TCPConnectionString)) { conn.Open(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlSchemaInfoTest/SqlSchemaInfoTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlSchemaInfoTest/SqlSchemaInfoTest.cs index a7ef4993bb..adf6d533f3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlSchemaInfoTest/SqlSchemaInfoTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlSchemaInfoTest/SqlSchemaInfoTest.cs @@ -9,6 +9,7 @@ using System.Data.Common; using System.Linq; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -43,7 +44,7 @@ public static void TestGetSchema(bool openTransaction) Assert.Equal(1, dataBases.Rows.Count); Assert.Equal(firstDatabaseName, dataBases.Rows[0]["database_name"] as string); - string nonexistentDatabaseName = DataTestUtility.GenerateRandomCharacters("NonExistentDatabase_"); + string nonexistentDatabaseName = TestRandomUtilities.GenerateRandomCharacters("NonExistentDatabase_"); dataBases = conn.GetSchema("DATABASES", [nonexistentDatabaseName]); Assert.Equal(0, dataBases.Rows.Count); @@ -97,7 +98,7 @@ public static async Task TestGetSchemaAsync(bool openTransaction) Assert.Equal(1, dataBases.Rows.Count); Assert.Equal(firstDatabaseName, dataBases.Rows[0]["database_name"] as string); - string nonexistentDatabaseName = DataTestUtility.GenerateRandomCharacters("NonExistentDatabase_"); + string nonexistentDatabaseName = TestRandomUtilities.GenerateRandomCharacters("NonExistentDatabase_"); dataBases = await conn.GetSchemaAsync("DATABASES", [nonexistentDatabaseName]); Assert.Equal(0, dataBases.Rows.Count); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/DistributedTransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/DistributedTransactionTest.cs index 7d1098ccad..a3aef87633 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/DistributedTransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/DistributedTransactionTest.cs @@ -170,7 +170,7 @@ private static async Task RunTestSet(Func TestCase) builder.Enlist = true; ConnectionString = builder.ConnectionString; - TestTableName = DataTestUtility.GenerateObjectName(); + TestTableName = DataTestUtility.GetShortName("DistributedTransactionTest"); DataTestUtility.RunNonQuery(ConnectionString, $"create table {TestTableName} (col1 int, col2 text)"); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs index 527665d2b1..8c3504faa3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs @@ -221,7 +221,7 @@ private static void RunTestSet(Action TestCase) private static void RunTestFormat(Action testCase) { - TestTableName = DataTestUtility.GenerateObjectName(); + TestTableName = DataTestUtility.GetShortName("TransactionEnlistmentTest"); DataTestUtility.RunNonQuery(ConnectionString, $"create table {TestTableName} (col1 int, col2 text)"); try { diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index 6b1dda8ae0..e4f022ebec 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -27,8 +27,6 @@ public class Config public string? TCPConnectionStringAASSGX = null; public bool EnclaveEnabled = false; public bool TracingEnabled = false; - public string? AADAuthorityURL = null; - public string? AADPasswordConnectionString = null; public string? AADServicePrincipalId = null; public string? AADServicePrincipalSecret = null; public string? AzureKeyVaultURL = null; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc index b2200b03c0..127b9642c9 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.jsonc @@ -1,5 +1,6 @@ { - // Typical Windows SQL Server on the local host via TCP. + // Typical Windows SQL Server on the local host via TCP + // or an Azure SQL Database connection string without any authentication or credential properties. "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;", // Typical Linux SQL Server on the local host, WSL, or a container, via TCP. @@ -7,14 +8,11 @@ // Typical Windows SQL Server on the local host via Named Pipes. "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;", - "TCPConnectionStringHGSVBS": "", "TCPConnectionStringNoneVBS": "", "TCPConnectionStringAASSGX": "", "EnclaveEnabled": false, "TracingEnabled": false, - "AADAuthorityURL": "", - "AADPasswordConnectionString": "", "AADServicePrincipalId": "", "AADServicePrincipalSecret": "", "AzureKeyVaultURL": "",