From b974ffbec7cc7e7c15bfa3ddc133b414abcc9a9d Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 10 Sep 2021 18:13:28 -0500 Subject: [PATCH 01/33] POC for TokenCredential support --- .gitignore | 2 + src/Microsoft.Data.SqlClient.sln | 15 ++++ ...reActiveDirectoryAuthenticationProvider.cs | 89 +++++++++++++++++++ ...Data.SqlClient.AzureAadAuthProvider.csproj | 28 ++++++ .../Data/Common/DbConnectionStringCommon.cs | 13 ++- .../SqlClient/SqlInternalConnectionTds.cs | 3 + .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 8 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 3 + .../Data/Common/DbConnectionStringCommon.cs | 6 +- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 8 +- tools/props/Versions.props | 2 +- 12 files changed, 171 insertions(+), 7 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj diff --git a/.gitignore b/.gitignore index daeaf68c7a..a4b7df5045 100644 --- a/.gitignore +++ b/.gitignore @@ -361,3 +361,5 @@ MigrationBackup/ # Config Json file **/config.json + +.idea/ diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index e3a765ce11..fc935c0db8 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -200,6 +200,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Do EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.PerformanceTests", "Microsoft.Data.SqlClient\tests\PerformanceTests\Microsoft.Data.SqlClient.PerformanceTests.csproj", "{599A336B-2A5F-473D-8442-1223ED37C93E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.AzureAadAuthProvider", "Microsoft.Data.SqlClient\add-ons\AzureActiveDirectoryAuthenticationProvider\Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj", "{48800BF7-13D9-48C9-A2CC-6F8017DF21B6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -472,6 +474,18 @@ Global {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x64.Build.0 = Release|x64 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.ActiveCfg = Release|x86 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.Build.0 = Release|x86 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x64.ActiveCfg = Debug|x64 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x64.Build.0 = Debug|x64 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x86.ActiveCfg = Debug|x86 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x86.Build.0 = Debug|x86 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|Any CPU.Build.0 = Release|Any CPU + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x64.ActiveCfg = Release|x64 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x64.Build.0 = Release|x64 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x86.ActiveCfg = Release|x86 + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -507,6 +521,7 @@ Global {B499E477-C9B1-4087-A5CF-5C762D90E433} = {0CC4817A-12F3-4357-912C-09315FAAD008} {B93A3149-67E8-491E-A1E5-19D65F9D9E98} = {0CC4817A-12F3-4357-912C-09315FAAD008} {599A336B-2A5F-473D-8442-1223ED37C93E} = {0CC4817A-12F3-4357-912C-09315FAAD008} + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6} = {C9726AED-D6A3-4AAC-BA04-92DD1F079594} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs new file mode 100644 index 0000000000..c67f379f43 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; + +namespace Microsoft.Data.SqlClient +{ + /// + /// + /// + public sealed class AzureActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider + { + private static readonly string s_defaultScopeSuffix = "/.default"; + private readonly string _type = typeof(AzureActiveDirectoryAuthenticationProvider).Name; + private readonly SqlClientLogger _logger = new(); + // private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; + private readonly TokenCredential _credential; + + /// + /// + /// + public AzureActiveDirectoryAuthenticationProvider(TokenCredential credential) + { + _credential = credential; + } + + /// + /// + /// + /// + /// + public override bool IsSupported(SqlAuthenticationMethod authentication) => authentication switch + { + SqlAuthenticationMethod.ActiveDirectoryTokenCredential => true, + SqlAuthenticationMethod.ActiveDirectoryIntegrated => true, + SqlAuthenticationMethod.ActiveDirectoryPassword => true, + SqlAuthenticationMethod.ActiveDirectoryInteractive => true, + SqlAuthenticationMethod.ActiveDirectoryServicePrincipal => true, + SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow => true, + SqlAuthenticationMethod.ActiveDirectoryManagedIdentity => true, + SqlAuthenticationMethod.ActiveDirectoryMSI => true, + SqlAuthenticationMethod.ActiveDirectoryDefault => true, + _ => false + }; + + /// + /// + /// + /// + public override void BeforeLoad(SqlAuthenticationMethod authentication) + { + _logger.LogInfo(_type, "BeforeLoad", $"being loaded into SqlAuthProviders for {authentication}."); + } + + /// + /// + /// + /// + public override void BeforeUnload(SqlAuthenticationMethod authentication) + { + _logger.LogInfo(_type, "BeforeUnload", $"being unloaded from SqlAuthProviders for {authentication}."); + } + + /// + /// + /// + /// + /// + /// + public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters) + { + CancellationTokenSource cts = new(); + + // Use Connection timeout value to cancel token acquire request after certain period of time. + cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds + + string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix; + string[] scopes = { scope }; + TokenRequestContext tokenRequestContext = new(scopes); + string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; + AccessToken accessToken = await _credential.GetTokenAsync(tokenRequestContext, cts.Token); + return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj new file mode 100644 index 0000000000..2e797dbdd9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj @@ -0,0 +1,28 @@ + + + Microsoft.Data.SqlClient.AzureAadAuthenticationProvider + Microsoft.Data.SqlClient.AzureAadAuthenticationProvider + AzureAadAuthenticationProvider + {48800BF7-13D9-48C9-A2CC-6F8017DF21B6} + netcoreapp + netfx + Debug;Release; + AnyCPU;x86;x64 + $(ObjFolder)$(Configuration).$(Platform)\$(AddOnName) + $(BinFolder)$(Configuration).$(Platform)\$(AddOnName) + $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName).xml + + false + MIT + true + + + + + + + + + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 0530df9cac..dc26a48a7b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -109,6 +109,7 @@ internal static string ConvertToString(object value) internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; internal const string ActiveDirectoryDefaultString = "Active Directory Default"; + internal const string ActiveDirectoryTokenCredentialString = "Active Directory TokenCredential"; #if DEBUG private static string[] s_supportedAuthenticationModes = @@ -122,7 +123,8 @@ internal static string ConvertToString(object value) "ActiveDirectoryDeviceCodeFlow", "ActiveDirectoryManagedIdentity", "ActiveDirectoryMSI", - "ActiveDirectoryDefault" + "ActiveDirectoryDefault", + "ActiveDirectoryTokenCredential" }; private static bool IsValidAuthenticationMethodEnum() @@ -204,6 +206,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryDefault; isSuccess = true; } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryTokenCredentialString) + || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryTokenCredential, CultureInfo.InvariantCulture))) + { + result = SqlAuthenticationMethod.ActiveDirectoryTokenCredential; + isSuccess = true; + } else { result = DbConnectionStringDefaults.Authentication; @@ -648,7 +656,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed"); + Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 11, "SqlAuthenticationMethod enum has changed, update needed"); return value == SqlAuthenticationMethod.SqlPassword || value == SqlAuthenticationMethod.ActiveDirectoryPassword || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated @@ -658,6 +666,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI || value == SqlAuthenticationMethod.ActiveDirectoryDefault + || value == SqlAuthenticationMethod.ActiveDirectoryTokenCredential || value == SqlAuthenticationMethod.NotSpecified; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index d46baf18e2..f574c73b53 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1315,6 +1315,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -2124,6 +2125,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); @@ -2369,6 +2371,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: case SqlAuthenticationMethod.ActiveDirectoryDefault: + case SqlAuthenticationMethod.ActiveDirectoryTokenCredential: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 728e521847..2f7ceb2edb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -253,6 +253,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes + public const byte MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -263,6 +264,7 @@ public enum ActiveDirectoryWorkflow : byte DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, + TokenCredential = MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1160,7 +1162,11 @@ public enum SqlAuthenticationMethod ActiveDirectoryMSI, /// - ActiveDirectoryDefault + ActiveDirectoryDefault, + /// + /// + /// + ActiveDirectoryTokenCredential } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 736610f8c2..60387feb5f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -7818,6 +7818,9 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD case SqlAuthenticationMethod.ActiveDirectoryDefault: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; break; + case SqlAuthenticationMethod.ActiveDirectoryTokenCredential: + workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL; + break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 0e1a941d06..4a431426f3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -540,7 +540,8 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj "ActiveDirectoryDeviceCodeFlow", "ActiveDirectoryManagedIdentity", "ActiveDirectoryMSI", - "ActiveDirectoryDefault" + "ActiveDirectoryDefault", + "ActiveDirectoryTokenCredential" }; private static bool IsValidAuthenticationMethodEnum() @@ -705,7 +706,7 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed"); + Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 11, "SqlAuthenticationMethod enum has changed, update needed"); return value == SqlAuthenticationMethod.SqlPassword || value == SqlAuthenticationMethod.ActiveDirectoryPassword || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated @@ -715,6 +716,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI || value == SqlAuthenticationMethod.ActiveDirectoryDefault + || value == SqlAuthenticationMethod.ActiveDirectoryTokenCredential #if ADONET_CERT_AUTH || value == SqlAuthenticationMethod.SqlCertificate #endif diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 13f0454c8c..fba25625ef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2572,6 +2572,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 20a7fad79e..0286a62c35 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -245,6 +245,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes + public const byte MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -255,6 +256,7 @@ public enum ActiveDirectoryWorkflow : byte DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, + TokenCredential = MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1124,8 +1126,12 @@ public enum SqlAuthenticationMethod /// ActiveDirectoryDefault, #if ADONET_CERT_AUTH - SqlCertificate + SqlCertificate, #endif + /// + /// + /// + ActiveDirectoryTokenCredential } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 88892e3f89..1349d4284a 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -50,7 +50,7 @@ - [1.6.0,2.0.0) + [1.19.0,2.0.0) [4.0.3,5.0.0) 5.0.0 From 8f76542a848ee29de8250965583b6b59aebef28f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 18 Oct 2021 15:49:42 -0500 Subject: [PATCH 02/33] POC 2 - callback abstraction --- src/Microsoft.Data.SqlClient.sln | 15 -------- ...Data.SqlClient.AzureAadAuthProvider.csproj | 28 --------------- .../src/Microsoft.Data.SqlClient.csproj | 4 +++ .../Data/SqlClient/AadTokenRequestContext.cs | 25 +++++++++++++ ...oryTokenCallbackAuthenticationProvider.cs} | 21 ++++------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 36 +++++++++++++++++-- 6 files changed, 70 insertions(+), 59 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs rename src/Microsoft.Data.SqlClient/{add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs => netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs} (70%) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index fc935c0db8..e3a765ce11 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -200,8 +200,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Do EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.PerformanceTests", "Microsoft.Data.SqlClient\tests\PerformanceTests\Microsoft.Data.SqlClient.PerformanceTests.csproj", "{599A336B-2A5F-473D-8442-1223ED37C93E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.AzureAadAuthProvider", "Microsoft.Data.SqlClient\add-ons\AzureActiveDirectoryAuthenticationProvider\Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj", "{48800BF7-13D9-48C9-A2CC-6F8017DF21B6}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -474,18 +472,6 @@ Global {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x64.Build.0 = Release|x64 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.ActiveCfg = Release|x86 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.Build.0 = Release|x86 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x64.ActiveCfg = Debug|x64 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x64.Build.0 = Debug|x64 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x86.ActiveCfg = Debug|x86 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Debug|x86.Build.0 = Debug|x86 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|Any CPU.Build.0 = Release|Any CPU - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x64.ActiveCfg = Release|x64 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x64.Build.0 = Release|x64 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x86.ActiveCfg = Release|x86 - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -521,7 +507,6 @@ Global {B499E477-C9B1-4087-A5CF-5C762D90E433} = {0CC4817A-12F3-4357-912C-09315FAAD008} {B93A3149-67E8-491E-A1E5-19D65F9D9E98} = {0CC4817A-12F3-4357-912C-09315FAAD008} {599A336B-2A5F-473D-8442-1223ED37C93E} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6} = {C9726AED-D6A3-4AAC-BA04-92DD1F079594} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj deleted file mode 100644 index 2e797dbdd9..0000000000 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/Microsoft.Data.SqlClient.AzureAadAuthProvider.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - Microsoft.Data.SqlClient.AzureAadAuthenticationProvider - Microsoft.Data.SqlClient.AzureAadAuthenticationProvider - AzureAadAuthenticationProvider - {48800BF7-13D9-48C9-A2CC-6F8017DF21B6} - netcoreapp - netfx - Debug;Release; - AnyCPU;x86;x64 - $(ObjFolder)$(Configuration).$(Platform)\$(AddOnName) - $(BinFolder)$(Configuration).$(Platform)\$(AddOnName) - $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName).xml - - false - MIT - true - - - - - - - - - - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index f42f2ecd05..65287109c8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -872,6 +872,10 @@ + + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs new file mode 100644 index 0000000000..ce242dbe72 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs @@ -0,0 +1,25 @@ +// 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.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// + /// + public class AadTokenRequestContext + { + /// + /// + /// + /// + public AadTokenRequestContext(string resource) { Resource = resource; } + + /// + /// + /// + public string Resource { get; } + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs similarity index 70% rename from src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs index c67f379f43..a0ac1f72f7 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureActiveDirectoryAuthenticationProvider/AzureActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs @@ -12,20 +12,18 @@ namespace Microsoft.Data.SqlClient /// /// /// - public sealed class AzureActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider + internal sealed class ActiveDirectoryTokenCallbackAuthenticationProvider : SqlAuthenticationProvider { - private static readonly string s_defaultScopeSuffix = "/.default"; - private readonly string _type = typeof(AzureActiveDirectoryAuthenticationProvider).Name; + private readonly Func> _tokenCallback; + private readonly string _type = typeof(ActiveDirectoryTokenCallbackAuthenticationProvider).Name; private readonly SqlClientLogger _logger = new(); - // private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; - private readonly TokenCredential _credential; /// /// /// - public AzureActiveDirectoryAuthenticationProvider(TokenCredential credential) + public ActiveDirectoryTokenCallbackAuthenticationProvider(Func> tokenCallback) { - _credential = credential; + _tokenCallback = tokenCallback; } /// @@ -73,17 +71,12 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) /// public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { + _logger.LogInfo(_type, nameof(AcquireTokenAsync), "Calling token callback."); CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time. cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds - - string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix; - string[] scopes = { scope }; - TokenRequestContext tokenRequestContext = new(scopes); - string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; - AccessToken accessToken = await _credential.GetTokenAsync(tokenRequestContext, cts.Token); - return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); + return await _tokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index f4594d3ca4..4036fa77e1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -89,6 +89,8 @@ private static readonly Dictionary /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + private Func> _accessTokenCallback; + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -270,7 +272,7 @@ internal static List GetColumnEncryptionSystemKeyStoreProvidersNames() } /// - /// This function returns a list of the names of the custom providers currently registered. If the + /// This function returns a list of the names of the custom providers currently registered. If the /// instance-level cache is not empty, that cache is used, else the global cache is used. /// /// Combined list of provider names @@ -342,7 +344,7 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary + /// + /// + public Func> AccessTokenCallback + { + get { return _accessTokenCallback; } + set + { + // If a connection is connecting or is ever opened, AccessToken callback cannot be set + if (!InnerConnection.AllowSetConnectionString) + { + throw ADP.OpenConnectionPropertySet(nameof(AccessTokenCallback), InnerConnection.State); + } + + if (value != null) + { + // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential + // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions); + } + if (value == null) + { + throw new ArgumentNullException(nameof(AccessTokenCallback), "Callback cannot be null."); + } + SqlAuthenticationProvider.SetProvider( + SqlAuthenticationMethod.ActiveDirectoryTokenCredential, + new ActiveDirectoryTokenCallbackAuthenticationProvider(value)); + _accessTokenCallback = value; + } + } + /// [ResDescription(StringsHelper.ResourceNames.SqlConnection_Database)] [ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)] From 9ed36d73cbd43ca7d3e29840b95bdda6bc976d6f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 22 Oct 2021 17:30:51 -0500 Subject: [PATCH 03/33] POC 3 --- .../src/Microsoft.Data.SqlClient.csproj | 1 - ...toryTokenCallbackAuthenticationProvider.cs | 82 ------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 5 +- .../Data/SqlClient/SqlConnectionFactory.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 205 +++++++++++------- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 2 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 11 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 +- .../Data/SqlClient/SqlConnectionPoolKey.cs | 29 ++- 9 files changed, 167 insertions(+), 175 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 9254631821..6c78413902 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -930,7 +930,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs deleted file mode 100644 index a0ac1f72f7..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryTokenCallbackAuthenticationProvider.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Azure.Core; - -namespace Microsoft.Data.SqlClient -{ - /// - /// - /// - internal sealed class ActiveDirectoryTokenCallbackAuthenticationProvider : SqlAuthenticationProvider - { - private readonly Func> _tokenCallback; - private readonly string _type = typeof(ActiveDirectoryTokenCallbackAuthenticationProvider).Name; - private readonly SqlClientLogger _logger = new(); - - /// - /// - /// - public ActiveDirectoryTokenCallbackAuthenticationProvider(Func> tokenCallback) - { - _tokenCallback = tokenCallback; - } - - /// - /// - /// - /// - /// - public override bool IsSupported(SqlAuthenticationMethod authentication) => authentication switch - { - SqlAuthenticationMethod.ActiveDirectoryTokenCredential => true, - SqlAuthenticationMethod.ActiveDirectoryIntegrated => true, - SqlAuthenticationMethod.ActiveDirectoryPassword => true, - SqlAuthenticationMethod.ActiveDirectoryInteractive => true, - SqlAuthenticationMethod.ActiveDirectoryServicePrincipal => true, - SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow => true, - SqlAuthenticationMethod.ActiveDirectoryManagedIdentity => true, - SqlAuthenticationMethod.ActiveDirectoryMSI => true, - SqlAuthenticationMethod.ActiveDirectoryDefault => true, - _ => false - }; - - /// - /// - /// - /// - public override void BeforeLoad(SqlAuthenticationMethod authentication) - { - _logger.LogInfo(_type, "BeforeLoad", $"being loaded into SqlAuthProviders for {authentication}."); - } - - /// - /// - /// - /// - public override void BeforeUnload(SqlAuthenticationMethod authentication) - { - _logger.LogInfo(_type, "BeforeUnload", $"being unloaded from SqlAuthProviders for {authentication}."); - } - - /// - /// - /// - /// - /// - /// - public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters) - { - _logger.LogInfo(_type, nameof(AcquireTokenAsync), "Calling token callback."); - CancellationTokenSource cts = new(); - - // Use Connection timeout value to cancel token acquire request after certain period of time. - cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds - return await _tokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 1f1c4662e6..52ddc48637 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -707,9 +707,8 @@ public Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -431,19 +432,19 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal // the new Login7 packet will always write out the new password (or a length of zero and no bytes if not present) // internal SqlInternalConnectionTds( - DbConnectionPoolIdentity identity, - SqlConnectionString connectionOptions, - SqlCredential credential, - object providerInfo, - string newPassword, - SecureString newSecurePassword, - bool redirectedUserInstance, - SqlConnectionString userConnectionOptions = null, // NOTE: userConnectionOptions may be different to connectionOptions if the connection string has been expanded (see SqlConnectionString.Expand) - SessionData reconnectSessionData = null, - bool applyTransientFaultHandling = false, - string accessToken = null, - DbConnectionPool pool = null - ) : base(connectionOptions) + DbConnectionPoolIdentity identity, + SqlConnectionString connectionOptions, + SqlCredential credential, + object providerInfo, + string newPassword, + SecureString newSecurePassword, + bool redirectedUserInstance, + SqlConnectionString userConnectionOptions = null, // NOTE: userConnectionOptions may be different to connectionOptions if the connection string has been expanded (see SqlConnectionString.Expand) + SessionData reconnectSessionData = null, + bool applyTransientFaultHandling = false, + string accessToken = null, + DbConnectionPool pool = null, + Func> accessTokenCallback = null) : base(connectionOptions) { #if DEBUG @@ -475,6 +476,10 @@ internal SqlInternalConnectionTds( { _accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken); } + if (accessTokenCallback != null) + { + _accessTokenCallback = accessTokenCallback; + } _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; @@ -1343,6 +1348,18 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, _federatedAuthenticationRequested = true; } + if (_accessTokenCallback != null) + { + requestedFeatures |= TdsEnums.FeatureExtension.FedAuth; + _fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData + { + libraryType = TdsEnums.FedAuthLibrary.SecurityTokenCallback, + fedAuthRequiredPreLoginResponse = _fedAuthRequired, + }; + // No need any further info from the server for token based authentication. So set _federatedAuthenticationRequested to true + _federatedAuthenticationInfoRequested = true; + } + // The GLOBALTRANSACTIONS, DATACLASSIFICATION, TCE, and UTF8 support features are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.GlobalTransactions | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.UTF8Support; @@ -2120,6 +2137,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) || _credential != null + || _accessTokenCallback != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity @@ -2321,7 +2339,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) string username = null; var authProvider = _sqlAuthenticationProviderManager.GetProvider(ConnectionOptions.Authentication); - if (authProvider == null) + if (authProvider == null && _accessTokenCallback == null) throw SQL.CannotFindAuthProvider(ConnectionOptions.Authentication.ToString()); // retry getting access token once if MsalException.error_code is unknown_error. @@ -2339,75 +2357,109 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) databaseName: ConnectionOptions.InitialCatalog) .WithConnectionId(_clientConnectionId) .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); - switch (ConnectionOptions.Authentication) + if (_accessTokenCallback != null) { - case SqlAuthenticationMethod.ActiveDirectoryIntegrated: - // In some scenarios for .NET Core, MSAL cannot detect the current user and needs it passed in - // for Integrated auth. Allow the user/application to pass it in to work around those scenarios. - if (!string.IsNullOrEmpty(ConnectionOptions.UserID)) - { - username = ConnectionOptions.UserID; - authParamsBuilder.WithUserId(username); - } - else - { - username = TdsEnums.NTAUTHORITYANONYMOUSLOGON; - } + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + authParamsBuilder.WithUserId(ConnectionOptions.UserID); + SqlAuthenticationParameters parameters = authParamsBuilder; + CancellationTokenSource cts = new(); + // Use Connection timeout value to cancel token acquire request after certain period of time. + cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + } + else + { + switch (ConnectionOptions.Authentication) + { + case SqlAuthenticationMethod.ActiveDirectoryIntegrated: + // In some scenarios for .NET Core, MSAL cannot detect the current user and needs it passed in + // for Integrated auth. Allow the user/application to pass it in to work around those scenarios. + if (!string.IsNullOrEmpty(ConnectionOptions.UserID)) + { + username = ConnectionOptions.UserID; + authParamsBuilder.WithUserId(username); + } + else + { + username = TdsEnums.NTAUTHORITYANONYMOUSLOGON; + } - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else - { - // We use Task.Run here in all places to execute task synchronously in the same context. - // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - break; - case SqlAuthenticationMethod.ActiveDirectoryInteractive: - case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: - case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: - case SqlAuthenticationMethod.ActiveDirectoryMSI: - case SqlAuthenticationMethod.ActiveDirectoryDefault: - case SqlAuthenticationMethod.ActiveDirectoryTokenCredential: - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else - { - authParamsBuilder.WithUserId(ConnectionOptions.UserID); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - break; - case SqlAuthenticationMethod.ActiveDirectoryPassword: - case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else - { - if (_credential != null) + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { - username = _credential.UserId; - authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; } else { - username = ConnectionOptions.UserID; - authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + // We use Task.Run here in all places to execute task synchronously in the same context. + // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - break; - default: - throw SQL.UnsupportedAuthenticationSpecified(ConnectionOptions.Authentication); + break; + case SqlAuthenticationMethod.ActiveDirectoryInteractive: + case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: + case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: + case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + authParamsBuilder.WithUserId(ConnectionOptions.UserID); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; + case SqlAuthenticationMethod.ActiveDirectoryPassword: + case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + if (_credential != null) + { + username = _credential.UserId; + authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + } + else + { + username = ConnectionOptions.UserID; + authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + } + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; + default: + throw SQL.UnsupportedAuthenticationSpecified(ConnectionOptions.Authentication); + } } Debug.Assert(_fedAuthToken.accessToken != null, "AccessToken should not be null."); @@ -2587,6 +2639,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) switch (_fedAuthFeatureExtensionData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: case TdsEnums.FedAuthLibrary.SecurityToken: // The server shouldn't have sent any additional data with the ack (like a nonce) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 2f7ceb2edb..8697bec889 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -236,6 +236,7 @@ public enum FeatureExtension : uint public const byte FEDAUTHLIB_LIVEID = 0X00; public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01; public const byte FEDAUTHLIB_MSAL = 0x02; + public const byte FEDAUTHLIB_SECURITYTOKEN_CALLBACK = 0x03; public const byte FEDAUTHLIB_RESERVED = 0X7F; public enum FedAuthLibrary : byte @@ -243,6 +244,7 @@ public enum FedAuthLibrary : byte LiveId = FEDAUTHLIB_LIVEID, SecurityToken = FEDAUTHLIB_SECURITYTOKEN, MSAL = FEDAUTHLIB_MSAL, + SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK, Default = FEDAUTHLIB_RESERVED } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index cc0fa1fed3..493dec47fa 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -935,7 +935,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus uint error = 0; // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || (_connHandler._accessTokenInBytes != null && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null )&& !trustServerCert); uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); @@ -1025,7 +1025,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus // Or AccessToken is not null, mean token based authentication is used. if ((_connHandler.ConnectionOptions != null && _connHandler.ConnectionOptions.Authentication != SqlAuthenticationMethod.NotSpecified) - || _connHandler._accessTokenInBytes != null) + || _connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null) { fedAuthRequired = payload[payloadOffset] == 0x01 ? true : false; } @@ -7752,7 +7752,7 @@ private static int StateValueLength(int dataLen) internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData, bool write /* if false just calculates the length */) { - Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.MSAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken, + Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.MSAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityTokenCallback, "only fed auth library type MSAL and Security Token are supported in writing feature request"); int dataLen = 0; @@ -7761,6 +7761,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set dataLen and totalLen switch (fedAuthFeatureData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: dataLen = 2; // length of feature data = 1 byte for library and echo + 1 byte for workflow break; @@ -7786,6 +7787,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set upper 7 bits of options to indicate fed auth library type switch (fedAuthFeatureData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_MSAL << 1; @@ -7848,6 +7850,9 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD WriteInt(fedAuthFeatureData.accessToken.Length, _physicalStateObj); _physicalStateObj.WriteByteArray(fedAuthFeatureData.accessToken, fedAuthFeatureData.accessToken.Length, 0); break; + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: + _physicalStateObj.WriteByte(TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL); + break; default: Debug.Fail("Unrecognized FedAuthLibrary type for feature extension request"); break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 2746f9c688..41fcda1540 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -522,6 +522,9 @@ Microsoft\Data\Common\AdapterUtil.cs + + Microsoft\Data\SqlClient\AadTokenRequestContext.cs + @@ -699,4 +702,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index c35ce6f08e..0f70079ae6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient @@ -14,9 +17,11 @@ internal class SqlConnectionPoolKey : DbConnectionPoolKey private int _hashValue; private readonly SqlCredential _credential; private readonly string _accessToken; + private Func> _accessTokenCallback; internal SqlCredential Credential => _credential; internal string AccessToken => _accessToken; + internal Func> AccessTokenCallback => _accessTokenCallback; internal override string ConnectionString { @@ -61,11 +66,12 @@ internal SqlConnectionPoolKey(string connectionString, #endregion #else #region NET Core - internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken) : base(connectionString) + internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) { - Debug.Assert(_credential == null || _accessToken == null, "Credential and AccessToken can't have the value at the same time."); + Debug.Assert(credential == null || accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; _accessToken = accessToken; + _accessTokenCallback = accessTokenCallback; CalculateHashCode(); } #endregion @@ -75,6 +81,7 @@ private SqlConnectionPoolKey(SqlConnectionPoolKey key) : base(key) { _credential = key.Credential; _accessToken = key.AccessToken; + _accessTokenCallback = key._accessTokenCallback; #if NETFRAMEWORK _serverCertificateValidationCallback = key._serverCertificateValidationCallback; _clientCertificateRetrievalCallback = key._clientCertificateRetrievalCallback; @@ -89,16 +96,15 @@ public override object Clone() public override bool Equals(object obj) { - return (obj is SqlConnectionPoolKey key - && _credential == key._credential - && ConnectionString == key.ConnectionString - && string.CompareOrdinal(_accessToken, key._accessToken) == 0 + return obj is SqlConnectionPoolKey key && + _credential == key._credential && + ConnectionString == key.ConnectionString && + string.CompareOrdinal(_accessToken, key._accessToken) == 0; #if NETFRAMEWORK && _serverCertificateValidationCallback == key._serverCertificateValidationCallback && _clientCertificateRetrievalCallback == key._clientCertificateRetrievalCallback - && _originalNetworkAddressInfo == key._originalNetworkAddressInfo + && _originalNetworkAddressInfo == key._originalNetworkAddressInf; #endif - ); } public override int GetHashCode() @@ -124,6 +130,13 @@ private void CalculateHashCode() _hashValue = _hashValue * 17 + _accessToken.GetHashCode(); } } + else if (_accessTokenCallback != null) + { + unchecked + { + _hashValue = _hashValue * 17 + _accessTokenCallback.GetHashCode(); + } + } #if NETFRAMEWORK if (_originalNetworkAddressInfo != null) From 832109026a76ef641b541097c863eaa492abad72 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 25 Oct 2021 09:07:25 -0500 Subject: [PATCH 04/33] fix --- .../Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index 0f70079ae6..e49382766c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -96,15 +96,16 @@ public override object Clone() public override bool Equals(object obj) { - return obj is SqlConnectionPoolKey key && - _credential == key._credential && - ConnectionString == key.ConnectionString && - string.CompareOrdinal(_accessToken, key._accessToken) == 0; + return (obj is SqlConnectionPoolKey key + && _credential == key._credential + && ConnectionString == key.ConnectionString + && string.CompareOrdinal(_accessToken, key._accessToken) == 0 #if NETFRAMEWORK && _serverCertificateValidationCallback == key._serverCertificateValidationCallback && _clientCertificateRetrievalCallback == key._clientCertificateRetrievalCallback - && _originalNetworkAddressInfo == key._originalNetworkAddressInf; + && _originalNetworkAddressInfo == key._originalNetworkAddressInfo #endif + ); } public override int GetHashCode() From 10f5a952adf84c3d7b1d317928095d98ee6d8a43 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 8 Nov 2021 16:45:09 -0600 Subject: [PATCH 05/33] merge --- .../src/Microsoft/Data/Common/AdapterUtil.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index f7e3715ccc..f274803cf0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -772,7 +772,8 @@ private static string ConnectionStateMsg(ConnectionState state) { // MDAC 82165, if the ConnectionState enum to msg the localization looks weird return state switch { - (ConnectionState.Closed) or (ConnectionState.Connecting | ConnectionState.Broken) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_Closed), + (ConnectionState.Closed) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_Closed), + (ConnectionState.Connecting | ConnectionState.Broken) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_Closed), (ConnectionState.Connecting) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_Connecting), (ConnectionState.Open) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_Open), (ConnectionState.Open | ConnectionState.Executing) => StringsHelper.GetString(Strings.ADP_ConnectionStateMsg_OpenExecuting), From d281983a6091d906a11887091fb76e2d2469b52e Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 12 Nov 2021 10:37:29 -0600 Subject: [PATCH 06/33] cleanups --- .../SqlClient/SqlInternalConnectionTds.cs | 245 ++++++++++-------- .../SqlClient/SqlInternalConnectionTds.cs | 1 - .../Data/Common/DbConnectionStringCommon.cs | 7 - .../Data/SqlClient/SqlConnectionPoolKey.cs | 7 +- 4 files changed, 147 insertions(+), 113 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 303e5af4d6..2eb9d50177 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1320,7 +1320,6 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault - || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -2350,116 +2349,116 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) try { var authParamsBuilder = new SqlAuthenticationParameters.Builder( - authenticationMethod: ConnectionOptions.Authentication, - resource: fedAuthInfo.spn, - authority: fedAuthInfo.stsurl, - serverName: ConnectionOptions.DataSource, - databaseName: ConnectionOptions.InitialCatalog) + authenticationMethod: ConnectionOptions.Authentication, + resource: fedAuthInfo.spn, + authority: fedAuthInfo.stsurl, + serverName: ConnectionOptions.DataSource, + databaseName: ConnectionOptions.InitialCatalog) .WithConnectionId(_clientConnectionId) .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); - if (_accessTokenCallback != null) - { - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else - { - authParamsBuilder.WithUserId(ConnectionOptions.UserID); - SqlAuthenticationParameters parameters = authParamsBuilder; - CancellationTokenSource cts = new(); - // Use Connection timeout value to cancel token acquire request after certain period of time. - cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - } - else + + switch (ConnectionOptions.Authentication) { - switch (ConnectionOptions.Authentication) - { - case SqlAuthenticationMethod.ActiveDirectoryIntegrated: - // In some scenarios for .NET Core, MSAL cannot detect the current user and needs it passed in - // for Integrated auth. Allow the user/application to pass it in to work around those scenarios. - if (!string.IsNullOrEmpty(ConnectionOptions.UserID)) - { - username = ConnectionOptions.UserID; - authParamsBuilder.WithUserId(username); - } - else - { - username = TdsEnums.NTAUTHORITYANONYMOUSLOGON; - } + case SqlAuthenticationMethod.ActiveDirectoryIntegrated: + // In some scenarios for .NET Core, MSAL cannot detect the current user and needs it passed in + // for Integrated auth. Allow the user/application to pass it in to work around those scenarios. + if (!string.IsNullOrEmpty(ConnectionOptions.UserID)) + { + username = ConnectionOptions.UserID; + authParamsBuilder.WithUserId(username); + } + else + { + username = TdsEnums.NTAUTHORITYANONYMOUSLOGON; + } - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + // We use Task.Run here in all places to execute task synchronously in the same context. + // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; + case SqlAuthenticationMethod.ActiveDirectoryInteractive: + case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: + case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: + case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + authParamsBuilder.WithUserId(ConnectionOptions.UserID); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; + case SqlAuthenticationMethod.ActiveDirectoryPassword: + case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + if (_credential != null) { - // We use Task.Run here in all places to execute task synchronously in the same context. - // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 + username = _credential.UserId; + authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) .GetAwaiter() .GetResult() .ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - break; - case SqlAuthenticationMethod.ActiveDirectoryInteractive: - case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: - case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: - case SqlAuthenticationMethod.ActiveDirectoryMSI: - case SqlAuthenticationMethod.ActiveDirectoryDefault: - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; } else { - authParamsBuilder.WithUserId(ConnectionOptions.UserID); + username = ConnectionOptions.UserID; + authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) .GetAwaiter() .GetResult() .ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; - } - break; - case SqlAuthenticationMethod.ActiveDirectoryPassword: - case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: - if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) - { - _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; - } - else - { - if (_credential != null) - { - username = _credential.UserId; - authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); - } - else - { - username = ConnectionOptions.UserID; - authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); - } - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } - break; - default: + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; + default: + if (_accessTokenCallback == null) + { throw SQL.UnsupportedAuthenticationSpecified(ConnectionOptions.Authentication); - } + } + + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + authParamsBuilder.WithUserId(ConnectionOptions.UserID); + SqlAuthenticationParameters parameters = authParamsBuilder; + CancellationTokenSource cts = new(); + // Use Connection timeout value to cancel token acquire request after certain period of time. + cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)) + .GetAwaiter() + .GetResult() + .ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; } Debug.Assert(_fedAuthToken.accessToken != null, "AccessToken should not be null."); @@ -2502,31 +2501,59 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Deal with normal MsalExceptions. catch (MsalException msalException) { - if (MsalError.UnknownError != msalException.ErrorCode - || _timeout.IsExpired - || _timeout.MillisecondsRemaining <= sleepInterval) + if (MsalError.UnknownError != msalException.ErrorCode || _timeout.IsExpired || _timeout.MillisecondsRemaining <= sleepInterval) { SqlClientEventSource.Log.TryTraceEvent(" {0}", msalException.ErrorCode); // Error[0] SqlErrorCollection sqlErs = new(); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + sqlErs.Add( + new SqlError( + 0, + (byte)0x00, + (byte)TdsEnums.MIN_ERROR_CLASS, + ConnectionOptions.DataSource, + StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, + 0)); // Error[1] string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + sqlErs.Add( + new SqlError( + 0, + (byte)0x00, + (byte)TdsEnums.MIN_ERROR_CLASS, + ConnectionOptions.DataSource, + errorMessage1, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, + 0)); // Error[2] if (!string.IsNullOrEmpty(msalException.Message)) { - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + sqlErs.Add( + new SqlError( + 0, + (byte)0x00, + (byte)TdsEnums.MIN_ERROR_CLASS, + ConnectionOptions.DataSource, + msalException.Message, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, + 0)); } SqlException exc = SqlException.CreateException(sqlErs, "", this); throw exc; } - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, remaining {1}[Milliseconds]", ObjectID, _timeout.MillisecondsRemaining); + SqlClientEventSource.Log.TryAdvancedTraceEvent( + " {0}, sleeping {1}[Milliseconds]", + ObjectID, + sleepInterval); + SqlClientEventSource.Log.TryAdvancedTraceEvent( + " {0}, remaining {1}[Milliseconds]", + ObjectID, + _timeout.MillisecondsRemaining); Thread.Sleep(sleepInterval); sleepInterval *= 2; @@ -2534,7 +2561,21 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // All other exceptions from MSAL/Azure Identity APIs catch (Exception e) { - throw SqlException.CreateException(new() { new(0, (byte)0x00, (byte)TdsEnums.FATAL_ERROR_CLASS, ConnectionOptions.DataSource, e.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0) }, "", this, e); + throw SqlException.CreateException( + new() + { + new( + 0, + (byte)0x00, + (byte)TdsEnums.FATAL_ERROR_CLASS, + ConnectionOptions.DataSource, + e.Message, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, + 0) + }, + "", + this, + e); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 35fafa57c5..623ce80d93 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2572,7 +2572,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault - || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index caff0d27f7..607709b2ea 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -347,7 +347,6 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; internal const string ActiveDirectoryDefaultString = "Active Directory Default"; - internal const string ActiveDirectoryTokenCredentialString = "Active Directory TokenCredential"; const string SqlCertificateString = "Sql Certificate"; #if DEBUG @@ -446,12 +445,6 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryDefault; isSuccess = true; } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryTokenCredentialString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryTokenCredential, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryTokenCredential; - isSuccess = true; - } #if ADONET_CERT_AUTH && NETFRAMEWORK else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlCertificateString) || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlCertificate, CultureInfo.InvariantCulture))) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index e49382766c..9d16054679 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -97,9 +97,10 @@ public override object Clone() public override bool Equals(object obj) { return (obj is SqlConnectionPoolKey key - && _credential == key._credential - && ConnectionString == key.ConnectionString - && string.CompareOrdinal(_accessToken, key._accessToken) == 0 + && _credential == key._credential + && ConnectionString == key.ConnectionString + && _accessTokenCallback == key._accessTokenCallback + && string.CompareOrdinal(_accessToken, key._accessToken) == 0 #if NETFRAMEWORK && _serverCertificateValidationCallback == key._serverCertificateValidationCallback && _clientCertificateRetrievalCallback == key._clientCertificateRetrievalCallback From fcfb66eea009dde11d374f8d83c9bec361207603 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 12 Nov 2021 10:42:55 -0600 Subject: [PATCH 07/33] formatting --- .../SqlClient/SqlInternalConnectionTds.cs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 2eb9d50177..13e4cd5dfc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2380,10 +2380,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { // We use Task.Run here in all places to execute task synchronously in the same context. // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2399,10 +2396,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2418,19 +2412,13 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { username = _credential.UserId; authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } else { username = ConnectionOptions.UserID; authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } @@ -2452,10 +2440,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time. cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)) - .GetAwaiter() - .GetResult() - .ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; From 4c718b5a6893d5f0db38b7c3f8df6938184d0c56 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 12 Nov 2021 13:59:35 -0600 Subject: [PATCH 08/33] netfx consistency --- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 31 +++++++++++++++++++ .../Data/SqlClient/SqlConnectionFactory.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 21 ++++++++++++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 2 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 12 +++++-- .../Data/SqlClient/SqlConnectionPoolKey.cs | 4 ++- 8 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 13e4cd5dfc..5fff5ec95c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -476,6 +476,7 @@ internal SqlInternalConnectionTds( { _accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken); } + if (accessTokenCallback != null) { _accessTokenCallback = accessTokenCallback; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 493dec47fa..d5af745628 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -935,7 +935,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus uint error = 0; // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null )&& !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null ) && !trustServerCert); uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index b6c2b5d930..9208618657 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -72,6 +72,8 @@ private static Dictionary s_systemC /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + private Func> _accessTokenCallback; + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -734,6 +736,35 @@ public string AccessToken } } + /// + /// + /// + public Func> AccessTokenCallback + { + get { return _accessTokenCallback; } + set + { + // If a connection is connecting or is ever opened, AccessToken callback cannot be set + if (!InnerConnection.AllowSetConnectionString) + { + throw ADP.OpenConnectionPropertySet(nameof(AccessTokenCallback), InnerConnection.State); + } + + if (value != null) + { + // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential + // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions); + } + if (value == null) + { + throw new ArgumentNullException(nameof(AccessTokenCallback), "Callback cannot be null."); + } + + ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, null, _serverCertificateValidationCallback, _clientCertificateRetrievalCallback, _originalNetworkAddressInfo, value)); + _accessTokenCallback = value; + } + } + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index e2051784a3..aca7bfc151 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -142,7 +142,7 @@ override protected DbConnectionInternal CreateConnection(DbConnectionOptions opt opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */); poolGroupProviderInfo = null; // null so we do not pass to constructor below... } - result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, key.ServerCertificateValidationCallback, key.ClientCertificateRetrievalCallback, pool, key.AccessToken, key.OriginalNetworkAddressInfo, applyTransientFaultHandling: applyTransientFaultHandling); + result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, key.ServerCertificateValidationCallback, key.ClientCertificateRetrievalCallback, pool, key.AccessToken, key.OriginalNetworkAddressInfo, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessTokenCallback); } return result; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 623ce80d93..bc99e84aa3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -134,6 +134,7 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -430,7 +431,8 @@ internal SqlInternalConnectionTds( DbConnectionPool pool = null, string accessToken = null, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo = null, - bool applyTransientFaultHandling = false) : base(connectionOptions) + bool applyTransientFaultHandling = false, + Func> accessTokenCallback = null) : base(connectionOptions) { #if DEBUG @@ -485,6 +487,11 @@ internal SqlInternalConnectionTds( _accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken); } + if (accessTokenCallback != null) + { + _accessTokenCallback = accessTokenCallback; + } + _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; @@ -1614,6 +1621,18 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, _federatedAuthenticationRequested = true; } + if (_accessTokenCallback != null) + { + requestedFeatures |= TdsEnums.FeatureExtension.FedAuth; + _fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData + { + libraryType = TdsEnums.FedAuthLibrary.SecurityTokenCallback, + fedAuthRequiredPreLoginResponse = _fedAuthRequired, + }; + // No need any further info from the server for token based authentication. So set _federatedAuthenticationRequested to true + _federatedAuthenticationInfoRequested = true; + } + // The TCE, DATACLASSIFICATION and GLOBALTRANSACTIONS, UTF8 support feature are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.GlobalTransactions; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 0286a62c35..5d954f5b23 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -228,6 +228,7 @@ public enum FeatureExtension : uint public const byte FEDAUTHLIB_LIVEID = 0X00; public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01; public const byte FEDAUTHLIB_MSAL = 0x02; + public const byte FEDAUTHLIB_SECURITYTOKEN_CALLBACK = 0x03; public const byte FEDAUTHLIB_RESERVED = 0X7F; public enum FedAuthLibrary : byte @@ -235,6 +236,7 @@ public enum FedAuthLibrary : byte LiveId = FEDAUTHLIB_LIVEID, SecurityToken = FEDAUTHLIB_SECURITYTOKEN, MSAL = FEDAUTHLIB_MSAL, + SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK, Default = FEDAUTHLIB_RESERVED } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 56ff3c94c6..bd37d4fcbc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1275,7 +1275,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod UInt32 error = 0; // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || (_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null) && !trustServerCert); UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (isYukonOrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); @@ -1428,7 +1428,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod // Or AccessToken is not null, mean token based authentication is used. if ((_connHandler.ConnectionOptions != null && _connHandler.ConnectionOptions.Authentication != SqlAuthenticationMethod.NotSpecified) - || _connHandler._accessTokenInBytes != null) + || _connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null) { fedAuthRequired = payload[payloadOffset] == 0x01 ? true : false; } @@ -8550,7 +8550,7 @@ static private int StateValueLength(int dataLen) internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData, bool write /* if false just calculates the length */) { - Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.MSAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken, + Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.MSAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityTokenCallback, "only fed auth library type MSAL and Security Token are supported in writing feature request"); int dataLen = 0; @@ -8559,6 +8559,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set dataLen and totalLen switch (fedAuthFeatureData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: dataLen = 2; // length of feature data = 1 byte for library and echo + 1 byte for workflow break; @@ -8584,6 +8585,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set upper 7 bits of options to indicate fed auth library type switch (fedAuthFeatureData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_MSAL << 1; @@ -8592,6 +8594,10 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD Debug.Assert(_connHandler._federatedAuthenticationRequested == true, "_federatedAuthenticationRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_SECURITYTOKEN << 1; break; + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: + Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); + options |= TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL << 1; + break; default: Debug.Fail("Unrecognized FedAuthLibrary type for feature extension request"); break; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index 9d16054679..29d966ba6a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -53,11 +53,13 @@ internal SqlConnectionPoolKey(string connectionString, string accessToken, ServerCertificateValidationCallback serverCertificateValidationCallback, ClientCertificateRetrievalCallback clientCertificateRetrievalCallback, - SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo) : base(connectionString) + SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, + Func> accessTokenCallback = null) : base(connectionString) { Debug.Assert(_credential == null || _accessToken == null, "Credential and AccessToken can't have the value at the same time."); _credential = credential; _accessToken = accessToken; + _accessTokenCallback = accessTokenCallback; _serverCertificateValidationCallback = serverCertificateValidationCallback; _clientCertificateRetrievalCallback = clientCertificateRetrievalCallback; _originalNetworkAddressInfo = originalNetworkAddressInfo; From 4ccc412c0eb5217f7210b1c3373757bfee1897f0 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 12 Nov 2021 14:26:44 -0600 Subject: [PATCH 09/33] fix --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index bd37d4fcbc..397f7e6acc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1275,7 +1275,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod UInt32 error = 0; // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || (_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null) && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || (_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null)) && !trustServerCert); UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (isYukonOrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); @@ -8585,7 +8585,6 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set upper 7 bits of options to indicate fed auth library type switch (fedAuthFeatureData.libraryType) { - case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_MSAL << 1; From 68bf51156e9600731b4b62ce8f3e5ab5d072969c Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 15 Nov 2021 13:25:24 -0600 Subject: [PATCH 10/33] cleanup --- .../Data/SqlClient/SqlInternalConnectionTds.cs | 1 - .../netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs | 10 +++------- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 3 --- .../netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs | 4 ++-- .../Microsoft/Data/Common/DbConnectionStringCommon.cs | 1 - .../tests/ManualTests/DataCommon/DataTestUtility.cs | 2 +- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 5fff5ec95c..37299eb146 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2143,7 +2143,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault - || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryTokenCredential || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 8697bec889..4f30effa33 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -244,8 +244,8 @@ public enum FedAuthLibrary : byte LiveId = FEDAUTHLIB_LIVEID, SecurityToken = FEDAUTHLIB_SECURITYTOKEN, MSAL = FEDAUTHLIB_MSAL, - SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK, - Default = FEDAUTHLIB_RESERVED + Default = FEDAUTHLIB_RESERVED, + SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK } public const byte MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; @@ -1164,11 +1164,7 @@ public enum SqlAuthenticationMethod ActiveDirectoryMSI, /// - ActiveDirectoryDefault, - /// - /// - /// - ActiveDirectoryTokenCredential + ActiveDirectoryDefault } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index d5af745628..10df9ec6e2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -7836,9 +7836,6 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD case SqlAuthenticationMethod.ActiveDirectoryDefault: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; break; - case SqlAuthenticationMethod.ActiveDirectoryTokenCredential: - workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL; - break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 5d954f5b23..dbe78ecffc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -236,8 +236,8 @@ public enum FedAuthLibrary : byte LiveId = FEDAUTHLIB_LIVEID, SecurityToken = FEDAUTHLIB_SECURITYTOKEN, MSAL = FEDAUTHLIB_MSAL, - SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK, - Default = FEDAUTHLIB_RESERVED + Default = FEDAUTHLIB_RESERVED, + SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK } public const byte MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 607709b2ea..faaa4f4fdf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -527,7 +527,6 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI || value == SqlAuthenticationMethod.ActiveDirectoryDefault - || value == SqlAuthenticationMethod.ActiveDirectoryTokenCredential #if ADONET_CERT_AUTH && NETFRAMEWORK || value == SqlAuthenticationMethod.SqlCertificate #endif diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 27acea362d..1848686ee9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -188,7 +188,7 @@ public static IEnumerable GetConnectionStrings(bool withEnclave) private static string GenerateAccessToken(string authorityURL, string aADAuthUserID, string aADAuthPassword) { - return AcquireTokenAsync(authorityURL, aADAuthUserID, aADAuthPassword).Result; + return AcquireTokenAsync(authorityURL, aADAuthUserID, aADAuthPassword).GetAwaiter().GetResult(); } private static Task AcquireTokenAsync(string authorityURL, string userID, string password) => Task.Run(() => From d9570b39ecc07aa6de45f108aa7f7964beddf3c7 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Mar 2023 16:47:07 -0600 Subject: [PATCH 11/33] nuget --- src/NuGet.config | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NuGet.config b/src/NuGet.config index 5832a9da27..0202069cda 100644 --- a/src/NuGet.config +++ b/src/NuGet.config @@ -3,5 +3,6 @@ + From aa517b694fd19f9ac639624bfa45b9266a199d70 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Mar 2023 17:34:41 -0600 Subject: [PATCH 12/33] source ref --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index bd8d67b6e2..53f23787e6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -89,6 +89,9 @@ + + Microsoft\Data\SqlClient\AadTokenRequestContext.cs + Microsoft\Data\Common\ActivityCorrelator.cs @@ -735,4 +738,4 @@ - + \ No newline at end of file From c5def10a9fd7bcec75889e0cbbb83aa155260f36 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 10 Mar 2023 17:25:54 -0600 Subject: [PATCH 13/33] revert nuget.config change --- src/NuGet.config | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NuGet.config b/src/NuGet.config index 0202069cda..5832a9da27 100644 --- a/src/NuGet.config +++ b/src/NuGet.config @@ -3,6 +3,5 @@ - From 727406ffebdc41e886db63d190b02682c3522fbb Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 10 Mar 2023 17:29:03 -0600 Subject: [PATCH 14/33] Update src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs Co-authored-by: David Engel --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 80de00ab9e..4090eb7bc8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2466,7 +2466,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) SqlAuthenticationParameters parameters = authParamsBuilder; CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time. - cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds + cts.CancelAfter(_timeout.MillisecondsRemaining); _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } From 5c1b5ef7089fa4bb1f16c4470445269ded4c1cc1 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 13 Mar 2023 12:28:46 -0500 Subject: [PATCH 15/33] cast timeout to int --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 4090eb7bc8..d869a7ee64 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2465,8 +2465,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) authParamsBuilder.WithUserId(ConnectionOptions.UserID); SqlAuthenticationParameters parameters = authParamsBuilder; CancellationTokenSource cts = new(); - // Use Connection timeout value to cancel token acquire request after certain period of time. - cts.CancelAfter(_timeout.MillisecondsRemaining); + // Use Connection timeout value to cancel token acquire request after certain period of time.(int) + cts.CancelAfter((int)_timeout.MillisecondsRemaining); _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } From d82f3bba842ed5f1b1a737f28796722ca80abd7c Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 14 Apr 2023 15:21:55 -0500 Subject: [PATCH 16/33] PR feedback --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 46 +- .../SqlClient/SqlInternalConnectionTds.cs | 5 +- .../netcore/src/Resources/Strings.Designer.cs | 40 +- .../netcore/src/Resources/Strings.resx | 14 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 11 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 1191 ----------------- .../netfx/src/Resources/Strings.Designer.cs | 65 +- .../netfx/src/Resources/Strings.resx | 14 +- .../Data/Common/AdapterUtil.Windows.cs | 1 + .../src/Microsoft/Data/Common/AdapterUtil.cs | 17 +- .../Data/Common/DbConnectionStringCommon.cs | 5 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 1 - 12 files changed, 205 insertions(+), 1205 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 5c1ff0b3cf..0f8fd52256 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -584,7 +584,7 @@ public override string ConnectionString } set { - if (_credential != null || _accessToken != null) + if (_credential != null || _accessToken != null || _accessTokenCallback != null) { SqlConnectionString connectionOptions = new SqlConnectionString(value); if (_credential != null) @@ -624,6 +624,10 @@ public override string ConnectionString { CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions); } + else if (_accessTokenCallback != null) + { + CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(connectionOptions); + } } ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken)); _connectionString = value; // Change _connectionString value only after value is validated @@ -707,7 +711,7 @@ public Func diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index d869a7ee64..91a90ad8af 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2466,7 +2466,10 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) SqlAuthenticationParameters parameters = authParamsBuilder; CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time.(int) - cts.CancelAfter((int)_timeout.MillisecondsRemaining); + if (_timeout.MillisecondsRemaining < Int32.MaxValue) + { + cts.CancelAfter((int)_timeout.MillisecondsRemaining); + } _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index ccbc9db180..ea913abea4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -429,6 +429,15 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenAndIntegratedSecurity { } } + /// + /// Looks up a localized string similar to Cannot set the AccessToken property if the AccessTokenCallback has been set.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. /// @@ -438,6 +447,24 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenAndUserIDPassword { } } + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the Credential property if the AccessToken property is already set.. /// @@ -447,6 +474,15 @@ internal static string ADP_InvalidMixedUsageOfCredentialAndAccessToken { } } + /// + /// Looks up a localized string similar to Cannot set the Credential property if the AccessTokenCallback property is already set.. + /// + internal static string ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot use Credential with UserID, UID, Password, or PWD connection string keywords.. /// @@ -950,7 +986,7 @@ internal static string Data_InvalidOffsetLength { return ResourceManager.GetString("Data_InvalidOffsetLength", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services.. /// @@ -1915,7 +1951,7 @@ internal static string SNI_ERROR_9 { } /// - /// Looks up a localized string similar to Incorrect physicalConnection type. + /// Looks up a localized string similar to Incorrect physicalConnection type.. /// internal static string SNI_IncorrectPhysicalConnectionType { get { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index f268baa2de..9d164fab39 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1941,4 +1941,16 @@ Encrypt=Strict is not supported when targeting .NET Standard 2.0. Use .NET Standard 2.1, .NET Framework, or .NET. - + + Cannot set the AccessToken property if the AccessTokenCallback has been set. + + + Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. + + + Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. + + + Cannot set the Credential property if the AccessTokenCallback property is already set. + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 53f23787e6..69bea3505f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -690,7 +690,16 @@ ResXFileCodeGenerator $(ResxFileName).Designer.cs - + + + + + + + + + + Microsoft.Data.SqlClient.SqlMetaData.xml PreserveNewest diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs deleted file mode 100644 index dbe78ecffc..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ /dev/null @@ -1,1191 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data; - -namespace Microsoft.Data.SqlClient -{ - /// Class of variables for the Tds connection. - /// - internal static class TdsEnums - { - - // internal tdsparser constants - - public const short SQL_SERVER_VERSION_SEVEN = 7; - - public const string SQL_PROVIDER_NAME = Common.DbConnectionStringDefaults.ApplicationName; - - public static readonly Decimal SQL_SMALL_MONEY_MIN = new Decimal(-214748.3648); - public static readonly Decimal SQL_SMALL_MONEY_MAX = new Decimal(214748.3647); - - // sql debugging constants, sdci is the structure passed in - public const string SDCI_MAPFILENAME = "SqlClientSSDebug"; - public const byte SDCI_MAX_MACHINENAME = 32; - public const byte SDCI_MAX_DLLNAME = 16; - public const byte SDCI_MAX_DATA = 255; - public const int SQLDEBUG_OFF = 0; - public const int SQLDEBUG_ON = 1; - public const int SQLDEBUG_CONTEXT = 2; - public const string SP_SDIDEBUG = "sp_sdidebug"; - public static readonly string[] SQLDEBUG_MODE_NAMES = new string[3] { - "off", - "on", - "context" - }; - - // HACK!!! - // Constant for SqlDbType.SmallVarBinary... store internal variable here instead of on - // SqlDbType so that it is not surfaced to the user!!! Related to dtc and the fact that - // the TransactionManager TDS stream is the only token left that uses VarBinarys instead of - // BigVarBinarys. - public const SqlDbType SmallVarBinary = (SqlDbType)(SqlDbType.Variant) + 1; - - // network protocol string constants - public const string TCP = "tcp"; - public const string NP = "np"; - public const string RPC = "rpc"; - public const string BV = "bv"; - public const string ADSP = "adsp"; - public const string SPX = "spx"; - public const string VIA = "via"; - public const string LPC = "lpc"; - - // network function string contants - public const string INIT_SSPI_PACKAGE = "InitSSPIPackage"; - public const string INIT_SESSION = "InitSession"; - public const string CONNECTION_GET_SVR_USER = "ConnectionGetSvrUser"; - public const string GEN_CLIENT_CONTEXT = "GenClientContext"; - - // tdsparser packet handling constants - public const byte SOFTFLUSH = 0; - public const byte HARDFLUSH = 1; - public const byte IGNORE = 2; - - // header constants - public const int HEADER_LEN = 8; - public const int HEADER_LEN_FIELD_OFFSET = 2; - public const int SPID_OFFSET = 4; - public const int YUKON_HEADER_LEN = 12; //Yukon headers also include a MARS session id - public const int MARS_ID_OFFSET = 8; - public const int HEADERTYPE_QNOTIFICATION = 1; - public const int HEADERTYPE_MARS = 2; - public const int HEADERTYPE_TRACE = 3; - - // other various constants - public const int SUCCEED = 1; - public const int FAIL = 0; - public const short TYPE_SIZE_LIMIT = 8000; - public const int MIN_PACKET_SIZE = 512; - // Login packet can be no greater than 4k until server sends us env-change - // increasing packet size. - public const int DEFAULT_LOGIN_PACKET_SIZE = 4096; - public const int MAX_PRELOGIN_PAYLOAD_LENGTH = 1024; - public const int MAX_PACKET_SIZE = 32768; - public const int MAX_SERVER_USER_NAME = 256; // obtained from luxor - - // Severity 0 - 10 indicates informational (non-error) messages - // Severity 11 - 16 indicates errors that can be corrected by user (syntax errors, etc...) - // Severity 17 - 19 indicates failure due to insufficient resources in the server - // (max locks exceeded, not enough memory, other internal server limits reached, etc..) - // Severity 20 - 25 Severe problems with the server, connection terminated. - public const byte MIN_ERROR_CLASS = 11; // webdata 100667: This should actually be 11 - public const byte MAX_USER_CORRECTABLE_ERROR_CLASS = 16; - public const byte FATAL_ERROR_CLASS = 20; - - // Message types - public const byte MT_SQL = 1; // SQL command batch - public const byte MT_LOGIN = 2; // Login message for pre-Sphinx (before version 7.0) - public const byte MT_RPC = 3; // Remote procedure call - public const byte MT_TOKENS = 4; // Table response data stream - public const byte MT_BINARY = 5; // Unformatted binary response data (UNUSED) - public const byte MT_ATTN = 6; // Attention (break) signal - public const byte MT_BULK = 7; // Bulk load data - public const byte MT_FEDAUTH = 8; // Authentication token for federated authentication - public const byte MT_CLOSE = 9; // Close subchannel (UNUSED) - public const byte MT_ERROR = 10; // Protocol error detected - public const byte MT_ACK = 11; // Protocol acknowledgement (UNUSED) - public const byte MT_ECHO = 12; // Echo data (UNUSED) - public const byte MT_LOGOUT = 13; // Logout message (UNUSED) - public const byte MT_TRANS = 14; // Transaction Manager Interface - public const byte MT_OLEDB = 15; // ? (UNUSED) - public const byte MT_LOGIN7 = 16; // Login message for Sphinx (version 7) or later - public const byte MT_SSPI = 17; // SSPI message - public const byte MT_PRELOGIN = 18; // Pre-login handshake - - // Message status bits - public const byte ST_EOM = 0x1; // Packet is end-of-message - public const byte ST_AACK = 0x2; // Packet acknowledges attention (server to client) - public const byte ST_IGNORE = 0x2; // Ignore this event (client to server) - public const byte ST_BATCH = 0x4; // Message is part of a batch. - public const byte ST_RESET_CONNECTION = 0x8; // Exec sp_reset_connection prior to processing message - public const byte ST_RESET_CONNECTION_PRESERVE_TRANSACTION = 0x10; // reset prior to processing, with preserving local tx - - // TDS control tokens - public const byte SQLCOLFMT = 0xa1; - public const byte SQLPROCID = 0x7c; - public const byte SQLCOLNAME = 0xa0; - public const byte SQLTABNAME = 0xa4; - public const byte SQLCOLINFO = 0xa5; - public const byte SQLALTNAME = 0xa7; - public const byte SQLALTFMT = 0xa8; - public const byte SQLERROR = 0xaa; - public const byte SQLINFO = 0xab; - public const byte SQLRETURNVALUE = 0xac; - public const byte SQLRETURNSTATUS = 0x79; - public const byte SQLRETURNTOK = 0xdb; - public const byte SQLALTCONTROL = 0xaf; - public const byte SQLROW = 0xd1; - public const byte SQLNBCROW = 0xd2; // same as ROW with null-bit-compression support - public const byte SQLALTROW = 0xd3; - public const byte SQLDONE = 0xfd; - public const byte SQLDONEPROC = 0xfe; - public const byte SQLDONEINPROC = 0xff; - public const byte SQLOFFSET = 0x78; - public const byte SQLORDER = 0xa9; - public const byte SQLDEBUG_CMD = 0x60; - public const byte SQLLOGINACK = 0xad; - public const byte SQLFEATUREEXTACK = 0xae; // TDS 7.4 - feature ack - public const byte SQLSESSIONSTATE = 0xe4; // TDS 7.4 - connection resiliency session state - public const byte SQLENVCHANGE = 0xe3; // Environment change notification - public const byte SQLSECLEVEL = 0xed; // Security level token ??? - public const byte SQLROWCRC = 0x39; // ROWCRC datastream??? - public const byte SQLCOLMETADATA = 0x81; // Column metadata including name - public const byte SQLALTMETADATA = 0x88; // Alt column metadata including name - public const byte SQLSSPI = 0xed; // SSPI data - public const byte SQLFEDAUTHINFO = 0xee; // Info for client to generate fed auth token - public const byte SQLRESCOLSRCS = 0xa2; - public const byte SQLDATACLASSIFICATION = 0xa3; - - // Environment change notification streams - // TYPE on TDS ENVCHANGE token stream (from sql\ntdbms\include\odsapi.h) - // - public const byte ENV_DATABASE = 1; // Database changed - public const byte ENV_LANG = 2; // Language changed - public const byte ENV_CHARSET = 3; // Character set changed - public const byte ENV_PACKETSIZE = 4; // Packet size changed - public const byte ENV_LOCALEID = 5; // Unicode data sorting locale id - public const byte ENV_COMPFLAGS = 6; // Unicode data sorting comparison flags - public const byte ENV_COLLATION = 7; // SQL Collation - // The following are environment change tokens valid for Yukon or later. - public const byte ENV_BEGINTRAN = 8; // Transaction began - public const byte ENV_COMMITTRAN = 9; // Transaction committed - public const byte ENV_ROLLBACKTRAN = 10; // Transaction rolled back - public const byte ENV_ENLISTDTC = 11; // Enlisted in Distributed Transaction - public const byte ENV_DEFECTDTC = 12; // Defected from Distributed Transaction - public const byte ENV_LOGSHIPNODE = 13; // Realtime Log shipping primary node - public const byte ENV_PROMOTETRANSACTION = 15; // Promote Transaction - public const byte ENV_TRANSACTIONMANAGERADDRESS = 16; // Transaction Manager Address - public const byte ENV_TRANSACTIONENDED = 17; // Transaction Ended - public const byte ENV_SPRESETCONNECTIONACK = 18; // SP_Reset_Connection ack - public const byte ENV_USERINSTANCE = 19; // User Instance - public const byte ENV_ROUTING = 20; // Routing (ROR) information - - // done status stream bit masks - public const int DONE_MORE = 0x0001; // more command results coming - public const int DONE_ERROR = 0x0002; // error in command batch - public const int DONE_INXACT = 0x0004; // transaction in progress - public const int DONE_PROC = 0x0008; // done from stored proc - public const int DONE_COUNT = 0x0010; // count in done info - public const int DONE_ATTN = 0x0020; // oob ack - public const int DONE_INPROC = 0x0040; // like DONE_PROC except proc had error - public const int DONE_RPCINBATCH = 0x0080; // Done from RPC in batch - public const int DONE_SRVERROR = 0x0100; // Severe error in which resultset should be discarded - public const int DONE_FMTSENT = 0x8000; // fmt message sent, done_inproc req'd - - // Feature Extension - public const byte FEATUREEXT_TERMINATOR = 0xFF; - public const byte FEATUREEXT_SRECOVERY = 0x01; - public const byte FEATUREEXT_FEDAUTH = 0x02; - // 0x03 is for x_eFeatureExtensionId_Rcs - public const byte FEATUREEXT_TCE = 0x04; - public const byte FEATUREEXT_GLOBALTRANSACTIONS = 0x05; - // 0x06 is for x_eFeatureExtensionId_LoginToken - // 0x07 is for x_eFeatureExtensionId_ClientSideTelemetry - public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; - public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; - public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; - public const byte FEATUREEXT_SQLDNSCACHING = 0x0B; - - [Flags] - public enum FeatureExtension : uint - { - None = 0, - SessionRecovery = 1 << (TdsEnums.FEATUREEXT_SRECOVERY - 1), - FedAuth = 1 << (TdsEnums.FEATUREEXT_FEDAUTH - 1), - Tce = 1 << (TdsEnums.FEATUREEXT_TCE - 1), - GlobalTransactions = 1 << (TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS - 1), - AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), - DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), - UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), - SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1) - } - - public const uint UTF8_IN_TDSCOLLATION = 0x4000000; - - public const byte FEDAUTHLIB_LIVEID = 0X00; - public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01; - public const byte FEDAUTHLIB_MSAL = 0x02; - public const byte FEDAUTHLIB_SECURITYTOKEN_CALLBACK = 0x03; - public const byte FEDAUTHLIB_RESERVED = 0X7F; - - public enum FedAuthLibrary : byte - { - LiveId = FEDAUTHLIB_LIVEID, - SecurityToken = FEDAUTHLIB_SECURITYTOKEN, - MSAL = FEDAUTHLIB_MSAL, - Default = FEDAUTHLIB_RESERVED, - SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK - } - - public const byte MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYINTEGRATED = 0x02; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE = 0x03; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have - public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have - public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication - public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes - public const byte MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes - - public enum ActiveDirectoryWorkflow : byte - { - Password = MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD, - Integrated = MSALWORKFLOW_ACTIVEDIRECTORYINTEGRATED, - Interactive = MSALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE, - ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, - DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, - ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, - Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, - TokenCredential = MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL, - } - - // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. - public const string NTAUTHORITYANONYMOUSLOGON = @"NT Authority\Anonymous Logon"; - - // Loginrec defines - public const byte MAX_LOG_NAME = 30; // TDS 4.2 login rec max name length - public const byte MAX_PROG_NAME = 10; // max length of loginrec progran name - public const byte SEC_COMP_LEN = 8; // length of security compartments - public const byte MAX_PK_LEN = 6; // max length of TDS packet size - public const byte MAX_NIC_SIZE = 6; // The size of a MAC or client address - public const byte SQLVARIANT_SIZE = 2; // size of the fixed portion of a sql variant (type, cbPropBytes) - public const byte VERSION_SIZE = 4; // size of the tds version (4 unsigned bytes) - public const int CLIENT_PROG_VER = 0x06000000; // Client interface version - public const int YUKON_LOG_REC_FIXED_LEN = 0x5e; - // misc - public const int TEXT_TIME_STAMP_LEN = 8; - public const int COLLATION_INFO_LEN = 4; - - /* - public const byte INT4_LSB_HI = 0; // lsb is low byte (eg 68000) - // public const byte INT4_LSB_LO = 1; // lsb is low byte (eg VAX) - public const byte INT2_LSB_HI = 2; // lsb is low byte (eg 68000) - // public const byte INT2_LSB_LO = 3; // lsb is low byte (eg VAX) - public const byte FLT_IEEE_HI = 4; // lsb is low byte (eg 68000) - public const byte CHAR_ASCII = 6; // ASCII character set - public const byte TWO_I4_LSB_HI = 8; // lsb is low byte (eg 68000 - // public const byte TWO_I4_LSB_LO = 9; // lsb is low byte (eg VAX) - // public const byte FLT_IEEE_LO = 10; // lsb is low byte (eg MSDOS) - public const byte FLT4_IEEE_HI = 12; // IEEE 4-byte floating point -lsb is high byte - // public const byte FLT4_IEEE_LO = 13; // IEEE 4-byte floating point -lsb is low byte - public const byte TWO_I2_LSB_HI = 16; // lsb is high byte - // public const byte TWO_I2_LSB_LO = 17; // lsb is low byte - - public const byte LDEFSQL = 0; // server sends its default - public const byte LDEFUSER = 0; // regular old user - public const byte LINTEGRATED = 8; // integrated security login - */ - - /* Versioning scheme table: - - Client sends: - 0x70000000 -> Sphinx - 0x71000000 -> Shiloh RTM - 0x71000001 -> Shiloh SP1 - 0x72xx0002 -> Yukon RTM - - Server responds: - 0x07000000 -> Sphinx // Notice server response format is different for bwd compat - 0x07010000 -> Shiloh RTM // Notice server response format is different for bwd compat - 0x71000001 -> Shiloh SP1 - 0x72xx0002 -> Yukon RTM - */ - - // Pre Shiloh SP1 versioning scheme: - public const int SPHINXORSHILOH_MAJOR = 0x07; // The high byte (b3) is not sufficient to distinguish - public const int SPHINX_INCREMENT = 0x00; // Sphinx and Shiloh - public const int SHILOH_INCREMENT = 0x01; // So we need to look at the high-mid byte (b2) as well - public const int DEFAULT_MINOR = 0x0000; - - // Shiloh SP1 and beyond versioning scheme: - - // Majors: - public const int SHILOHSP1_MAJOR = 0x71; // For Shiloh SP1 and later the versioning schema changed and - public const int YUKON_MAJOR = 0x72; // the high-byte is sufficient to distinguish later versions - public const int KATMAI_MAJOR = 0x73; - public const int DENALI_MAJOR = 0x74; - - // Increments: - public const int SHILOHSP1_INCREMENT = 0x00; - public const int YUKON_INCREMENT = 0x09; - public const int KATMAI_INCREMENT = 0x0b; - public const int DENALI_INCREMENT = 0x00; - - // Minors: - public const int SHILOHSP1_MINOR = 0x0001; - public const int YUKON_RTM_MINOR = 0x0002; - public const int KATMAI_MINOR = 0x0003; - public const int DENALI_MINOR = 0x0004; - - public const int ORDER_68000 = 1; - public const int USE_DB_ON = 1; - public const int INIT_DB_FATAL = 1; - public const int SET_LANG_ON = 1; - public const int INIT_LANG_FATAL = 1; - public const int ODBC_ON = 1; - public const int SSPI_ON = 1; - public const int REPL_ON = 3; - - - // send the read-only intent to the server - public const int READONLY_INTENT_ON = 1; - - // Token masks - public const byte SQLLenMask = 0x30; // mask to check for length tokens - public const byte SQLFixedLen = 0x30; // Mask to check for fixed token - public const byte SQLVarLen = 0x20; // Value to check for variable length token - public const byte SQLZeroLen = 0x10; // Value to check for zero length token - public const byte SQLVarCnt = 0x00; // Value to check for variable count token - - // Token masks for COLINFO status - public const byte SQLDifferentName = 0x20; // column name different than select list name - public const byte SQLExpression = 0x4; // column was result of an expression - public const byte SQLKey = 0x8; // column is part of the key for the table - public const byte SQLHidden = 0x10; // column not part of select list but added because part of key - - // Token masks for COLMETADATA flags - // first byte - public const byte Nullable = 0x1; - public const byte Identity = 0x10; - public const byte Updatability = 0xb; // mask off bits 3 and 4 - // second byte - public const byte ClrFixedLen = 0x1; // Fixed length CLR type - public const byte IsColumnSet = 0x4; // Column is an XML representation of an aggregation of other columns - public const byte IsEncrypted = 0x8; // Column is encrypted using TCE - - // null values - public const uint VARLONGNULL = 0xffffffff; // null value for text and image types - public const int VARNULL = 0xffff; // null value for character and binary types - public const int MAXSIZE = 8000; // max size for any column - public const byte FIXEDNULL = 0; - public const UInt64 UDTNULL = 0xffffffffffffffff; - - // SQL Server Data Type Tokens. - public const int SQLVOID = 0x1f; - public const int SQLTEXT = 0x23; - public const int SQLVARBINARY = 0x25; - public const int SQLINTN = 0x26; - public const int SQLVARCHAR = 0x27; - public const int SQLBINARY = 0x2d; - public const int SQLIMAGE = 0x22; - public const int SQLCHAR = 0x2f; - public const int SQLINT1 = 0x30; - public const int SQLBIT = 0x32; - public const int SQLINT2 = 0x34; - public const int SQLINT4 = 0x38; - public const int SQLMONEY = 0x3c; - public const int SQLDATETIME = 0x3d; - public const int SQLFLT8 = 0x3e; - public const int SQLFLTN = 0x6d; - public const int SQLMONEYN = 0x6e; - public const int SQLDATETIMN = 0x6f; - public const int SQLFLT4 = 0x3b; - public const int SQLMONEY4 = 0x7a; - public const int SQLDATETIM4 = 0x3a; - public const int SQLDECIMALN = 0x6a; - public const int SQLNUMERICN = 0x6c; - public const int SQLUNIQUEID = 0x24; - public const int SQLBIGCHAR = 0xaf; - public const int SQLBIGVARCHAR = 0xa7; - public const int SQLBIGBINARY = 0xad; - public const int SQLBIGVARBINARY = 0xa5; - public const int SQLBITN = 0x68; - public const int SQLNCHAR = 0xef; - public const int SQLNVARCHAR = 0xe7; - public const int SQLNTEXT = 0x63; - public const int SQLUDT = 0xF0; - - // aggregate operator type TDS tokens, used by compute statements: - public const int AOPCNTB = 0x09; - public const int AOPSTDEV = 0x30; - public const int AOPSTDEVP = 0x31; - public const int AOPVAR = 0x32; - public const int AOPVARP = 0x33; - - public const int AOPCNT = 0x4b; - public const int AOPSUM = 0x4d; - public const int AOPAVG = 0x4f; - public const int AOPMIN = 0x51; - public const int AOPMAX = 0x52; - public const int AOPANY = 0x53; - public const int AOPNOOP = 0x56; - - // SQL Server user-defined type tokens we care about - public const int SQLTIMESTAMP = 0x50; - - public const int MAX_NUMERIC_LEN = 0x11; // 17 bytes of data for max numeric/decimal length - public const int DEFAULT_NUMERIC_PRECISION = 0x1D; // 29 is the default max numeric precision(Decimal.MaxValue) if not user set - public const int SPHINX_DEFAULT_NUMERIC_PRECISION = 0x1C; // 28 is the default max numeric precision for Sphinx(Decimal.MaxValue doesn't work for sphinx) - public const int MAX_NUMERIC_PRECISION = 0x26; // 38 is max numeric precision; - public const byte UNKNOWN_PRECISION_SCALE = 0xff; // -1 is value for unknown precision or scale - - // The following datatypes are specific to SHILOH (version 8) and later. - public const int SQLINT8 = 0x7f; - public const int SQLVARIANT = 0x62; - - // The following datatypes are specific to Yukon (version 9) or later - public const int SQLXMLTYPE = 0xf1; - public const int XMLUNICODEBOM = 0xfeff; - public static readonly byte[] XMLUNICODEBOMBYTES = { 0xff, 0xfe }; - - // The following datatypes are specific to Katmai (version 10) or later - public const int SQLTABLE = 0xf3; - public const int SQLDATE = 0x28; - public const int SQLTIME = 0x29; - public const int SQLDATETIME2 = 0x2a; - public const int SQLDATETIMEOFFSET = 0x2b; - - public const int DEFAULT_VARTIME_SCALE = 7; - - //Partially length prefixed datatypes constants. These apply to XMLTYPE, BIGVARCHRTYPE, - // NVARCHARTYPE, and BIGVARBINTYPE. Valid for Yukon or later - - public const ulong SQL_PLP_NULL = 0xffffffffffffffff; // Represents null value - public const ulong SQL_PLP_UNKNOWNLEN = 0xfffffffffffffffe; // Data coming in chunks, total length unknown - public const int SQL_PLP_CHUNK_TERMINATOR = 0x00000000; // Represents end of chunked data. - public const ushort SQL_USHORTVARMAXLEN = 0xffff; // Second ushort in TDS stream is this value if one of max types - - // TVPs require some new in-value control tokens: - public const byte TVP_ROWCOUNT_ESTIMATE = 0x12; - public const byte TVP_ROW_TOKEN = 0x01; - public const byte TVP_END_TOKEN = 0x00; - public const ushort TVP_NOMETADATA_TOKEN = 0xFFFF; - public const byte TVP_ORDER_UNIQUE_TOKEN = 0x10; - - // TvpColumnMetaData flags - public const int TVP_DEFAULT_COLUMN = 0x200; - - // TVP_ORDER_UNIQUE_TOKEN flags - public const byte TVP_ORDERASC_FLAG = 0x1; - public const byte TVP_ORDERDESC_FLAG = 0x2; - public const byte TVP_UNIQUE_FLAG = 0x4; - - public const bool Is68K = false; - public const bool TraceTDS = false; - - // RPC function names - public const string SP_EXECUTESQL = "sp_executesql"; // used against 7.0 servers - public const string SP_PREPEXEC = "sp_prepexec"; // used against 7.5 servers - - public const string SP_PREPARE = "sp_prepare"; // used against 7.0 servers - public const string SP_EXECUTE = "sp_execute"; - public const string SP_UNPREPARE = "sp_unprepare"; - public const string SP_PARAMS = "sp_procedure_params_rowset"; - public const string SP_PARAMS_MANAGED = "sp_procedure_params_managed"; - public const string SP_PARAMS_MGD10 = "sp_procedure_params_100_managed"; - - // RPC ProcID's - // NOTE: It is more efficient to call these procs using ProcID's instead of names - public const ushort RPC_PROCID_CURSOR = 1; - public const ushort RPC_PROCID_CURSOROPEN = 2; - public const ushort RPC_PROCID_CURSORPREPARE = 3; - public const ushort RPC_PROCID_CURSOREXECUTE = 4; - public const ushort RPC_PROCID_CURSORPREPEXEC = 5; - public const ushort RPC_PROCID_CURSORUNPREPARE = 6; - public const ushort RPC_PROCID_CURSORFETCH = 7; - public const ushort RPC_PROCID_CURSOROPTION = 8; - public const ushort RPC_PROCID_CURSORCLOSE = 9; - public const ushort RPC_PROCID_EXECUTESQL = 10; - public const ushort RPC_PROCID_PREPARE = 11; - public const ushort RPC_PROCID_EXECUTE = 12; - public const ushort RPC_PROCID_PREPEXEC = 13; - public const ushort RPC_PROCID_PREPEXECRPC = 14; - public const ushort RPC_PROCID_UNPREPARE = 15; - - // For Transactions - public const string TRANS_BEGIN = "BEGIN TRANSACTION"; - public const string TRANS_COMMIT = "COMMIT TRANSACTION"; - public const string TRANS_ROLLBACK = "ROLLBACK TRANSACTION"; - public const string TRANS_IF_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"; - public const string TRANS_SAVE = "SAVE TRANSACTION"; - - // For Transactions - isolation levels - public const string TRANS_READ_COMMITTED = "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; - public const string TRANS_READ_UNCOMMITTED = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; - public const string TRANS_REPEATABLE_READ = "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"; - public const string TRANS_SERIALIZABLE = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"; - public const string TRANS_SNAPSHOT = "SET TRANSACTION ISOLATION LEVEL SNAPSHOT"; - - // Batch RPC flags - public const byte SHILOH_RPCBATCHFLAG = 0x80; - public const byte YUKON_RPCBATCHFLAG = 0xFF; - - // RPC flags - public const byte RPC_RECOMPILE = 0x1; - public const byte RPC_NOMETADATA = 0x2; - - // RPC parameter class - public const byte RPC_PARAM_BYREF = 0x1; - public const byte RPC_PARAM_DEFAULT = 0x2; - public const byte RPC_PARAM_ENCRYPTED = 0x8; - - // SQL parameter list text - public const string PARAM_OUTPUT = "output"; - - // SQL Parameter constants - public const int MAX_PARAMETER_NAME_LENGTH = 128; - - // metadata options (added around an existing sql statement) - - // prefixes - public const string FMTONLY_ON = " SET FMTONLY ON;"; - public const string FMTONLY_OFF = " SET FMTONLY OFF;"; - // suffixes - public const string BROWSE_ON = " SET NO_BROWSETABLE ON;"; - public const string BROWSE_OFF = " SET NO_BROWSETABLE OFF;"; - - // generic table name - public const string TABLE = "Table"; - - public const int EXEC_THRESHOLD = 0x3; // if the number of commands we execute is > than this threshold, than do prep/exec/unprep instead - // of executesql. - - // dbnetlib error values - public const short TIMEOUT_EXPIRED = -2; - public const short ENCRYPTION_NOT_SUPPORTED = 20; - public const short CTAIP_NOT_SUPPORTED = 21; - - // CAUTION: These are not error codes returned by SNI. This is used for backward compatibility - // since netlib (now removed from sqlclient) returned these codes. - - // SQL error values (from sqlerrorcodes.h) - public const int LOGON_FAILED = 18456; - public const int PASSWORD_EXPIRED = 18488; - public const int IMPERSONATION_FAILED = 1346; - public const int P_TOKENTOOLONG = 103; - - // SQL error that indicates retry for Always Encrypted - public const int TCE_CONVERSION_ERROR_CLIENT_RETRY = 33514; - public const int TCE_ENCLAVE_INVALID_SESSION_HANDLE = 33195; - - // SNI\Win32 error values - // NOTE: these are simply windows system error codes, not SNI specific - public const uint SNI_UNINITIALIZED = unchecked((uint)-1); - public const uint SNI_SUCCESS = 0; // The operation completed successfully. - public const uint SNI_WAIT_TIMEOUT = 258; // The wait operation timed out. - public const uint SNI_SUCCESS_IO_PENDING = 997; // Overlapped I/O operation is in progress. - - // Windows Sockets Error Codes - public const short SNI_WSAECONNRESET = 10054; // An existing connection was forcibly closed by the remote host. - - // SNI flags - public const UInt32 SNI_SSL_VALIDATE_CERTIFICATE = 1; // This enables validation of server certificate - public const UInt32 SNI_SSL_USE_SCHANNEL_CACHE = 2; // This enables schannel session cache - public const UInt32 SNI_SSL_IGNORE_CHANNEL_BINDINGS = 0x10; // Used with SSL Provider, sent to SNIAddProvider in case of SQL Authentication & Encrypt. - - public const string DEFAULT_ENGLISH_CODE_PAGE_STRING = "iso_1"; - public const short DEFAULT_ENGLISH_CODE_PAGE_VALUE = 1252; - public const short CHARSET_CODE_PAGE_OFFSET = 2; - internal const int MAX_SERVERNAME = 255; - - // Sql Statement Tokens in the DONE packet - // (see ntdbms\ntinc\tokens.h) - // - internal const ushort SELECT = 0xc1; - internal const ushort INSERT = 0xc3; - internal const ushort DELETE = 0xc4; - internal const ushort UPDATE = 0xc5; - internal const ushort ABORT = 0xd2; - internal const ushort BEGINXACT = 0xd4; - internal const ushort ENDXACT = 0xd5; - internal const ushort BULKINSERT = 0xf0; - internal const ushort OPENCURSOR = 0x20; - internal const ushort MERGE = 0x117; - - - // Login data validation Rules - // - internal const ushort MAXLEN_HOSTNAME = 128; // the client machine name - internal const ushort MAXLEN_CLIENTID = 128; - internal const ushort MAXLEN_CLIENTSECRET = 128; - internal const ushort MAXLEN_APPNAME = 128; // the client application name - internal const ushort MAXLEN_SERVERNAME = 128; // the server name - internal const ushort MAXLEN_CLIENTINTERFACE = 128; // the interface library name - internal const ushort MAXLEN_LANGUAGE = 128; // the initial language - internal const ushort MAXLEN_DATABASE = 128; // the initial database - internal const ushort MAXLEN_ATTACHDBFILE = 260; // the filename for a database that is to be attached during the connection process - internal const ushort MAXLEN_NEWPASSWORD = 128; // new password for the specified login. - - - // array copied directly from tdssort.h from luxor - public static readonly UInt16[] CODE_PAGE_FROM_SORT_ID = { - 0, /* 0 */ - 0, /* 1 */ - 0, /* 2 */ - 0, /* 3 */ - 0, /* 4 */ - 0, /* 5 */ - 0, /* 6 */ - 0, /* 7 */ - 0, /* 8 */ - 0, /* 9 */ - 0, /* 10 */ - 0, /* 11 */ - 0, /* 12 */ - 0, /* 13 */ - 0, /* 14 */ - 0, /* 15 */ - 0, /* 16 */ - 0, /* 17 */ - 0, /* 18 */ - 0, /* 19 */ - 0, /* 20 */ - 0, /* 21 */ - 0, /* 22 */ - 0, /* 23 */ - 0, /* 24 */ - 0, /* 25 */ - 0, /* 26 */ - 0, /* 27 */ - 0, /* 28 */ - 0, /* 29 */ - 437, /* 30 */ - 437, /* 31 */ - 437, /* 32 */ - 437, /* 33 */ - 437, /* 34 */ - 0, /* 35 */ - 0, /* 36 */ - 0, /* 37 */ - 0, /* 38 */ - 0, /* 39 */ - 850, /* 40 */ - 850, /* 41 */ - 850, /* 42 */ - 850, /* 43 */ - 850, /* 44 */ - 0, /* 45 */ - 0, /* 46 */ - 0, /* 47 */ - 0, /* 48 */ - 850, /* 49 */ - 1252, /* 50 */ - 1252, /* 51 */ - 1252, /* 52 */ - 1252, /* 53 */ - 1252, /* 54 */ - 850, /* 55 */ - 850, /* 56 */ - 850, /* 57 */ - 850, /* 58 */ - 850, /* 59 */ - 850, /* 60 */ - 850, /* 61 */ - 0, /* 62 */ - 0, /* 63 */ - 0, /* 64 */ - 0, /* 65 */ - 0, /* 66 */ - 0, /* 67 */ - 0, /* 68 */ - 0, /* 69 */ - 0, /* 70 */ - 1252, /* 71 */ - 1252, /* 72 */ - 1252, /* 73 */ - 1252, /* 74 */ - 1252, /* 75 */ - 0, /* 76 */ - 0, /* 77 */ - 0, /* 78 */ - 0, /* 79 */ - 1250, /* 80 */ - 1250, /* 81 */ - 1250, /* 82 */ - 1250, /* 83 */ - 1250, /* 84 */ - 1250, /* 85 */ - 1250, /* 86 */ - 1250, /* 87 */ - 1250, /* 88 */ - 1250, /* 89 */ - 1250, /* 90 */ - 1250, /* 91 */ - 1250, /* 92 */ - 1250, /* 93 */ - 1250, /* 94 */ - 1250, /* 95 */ - 1250, /* 96 */ - 1250, /* 97 */ - 1250, /* 98 */ - 0, /* 99 */ - 0, /* 100 */ - 0, /* 101 */ - 0, /* 102 */ - 0, /* 103 */ - 1251, /* 104 */ - 1251, /* 105 */ - 1251, /* 106 */ - 1251, /* 107 */ - 1251, /* 108 */ - 0, /* 109 */ - 0, /* 110 */ - 0, /* 111 */ - 1253, /* 112 */ - 1253, /* 113 */ - 1253, /* 114 */ - 0, /* 115 */ - 0, /* 116 */ - 0, /* 117 */ - 0, /* 118 */ - 0, /* 119 */ - 1253, /* 120 */ - 1253, /* 121 */ - 1253, /* 122 */ - 0, /* 123 */ - 1253, /* 124 */ - 0, /* 125 */ - 0, /* 126 */ - 0, /* 127 */ - 1254, /* 128 */ - 1254, /* 129 */ - 1254, /* 130 */ - 0, /* 131 */ - 0, /* 132 */ - 0, /* 133 */ - 0, /* 134 */ - 0, /* 135 */ - 1255, /* 136 */ - 1255, /* 137 */ - 1255, /* 138 */ - 0, /* 139 */ - 0, /* 140 */ - 0, /* 141 */ - 0, /* 142 */ - 0, /* 143 */ - 1256, /* 144 */ - 1256, /* 145 */ - 1256, /* 146 */ - 0, /* 147 */ - 0, /* 148 */ - 0, /* 149 */ - 0, /* 150 */ - 0, /* 151 */ - 1257, /* 152 */ - 1257, /* 153 */ - 1257, /* 154 */ - 1257, /* 155 */ - 1257, /* 156 */ - 1257, /* 157 */ - 1257, /* 158 */ - 1257, /* 159 */ - 1257, /* 160 */ - 0, /* 161 */ - 0, /* 162 */ - 0, /* 163 */ - 0, /* 164 */ - 0, /* 165 */ - 0, /* 166 */ - 0, /* 167 */ - 0, /* 168 */ - 0, /* 169 */ - 0, /* 170 */ - 0, /* 171 */ - 0, /* 172 */ - 0, /* 173 */ - 0, /* 174 */ - 0, /* 175 */ - 0, /* 176 */ - 0, /* 177 */ - 0, /* 178 */ - 0, /* 179 */ - 0, /* 180 */ - 0, /* 181 */ - 0, /* 182 */ - 1252, /* 183 */ - 1252, /* 184 */ - 1252, /* 185 */ - 1252, /* 186 */ - 0, /* 187 */ - 0, /* 188 */ - 0, /* 189 */ - 0, /* 190 */ - 0, /* 191 */ - 932, /* 192 */ - 932, /* 193 */ - 949, /* 194 */ - 949, /* 195 */ - 950, /* 196 */ - 950, /* 197 */ - 936, /* 198 */ - 936, /* 199 */ - 932, /* 200 */ - 949, /* 201 */ - 950, /* 202 */ - 936, /* 203 */ - 874, /* 204 */ - 874, /* 205 */ - 874, /* 206 */ - 0, /* 207 */ - 0, /* 208 */ - 0, /* 209 */ - 1252, /* 210 */ - 1252, /* 211 */ - 1252, /* 212 */ - 1252, /* 213 */ - 1252, /* 214 */ - 1252, /* 215 */ - 1252, /* 216 */ - 1252, /* 217 */ - 0, /* 218 */ - 0, /* 219 */ - 0, /* 220 */ - 0, /* 221 */ - 0, /* 222 */ - 0, /* 223 */ - 0, /* 224 */ - 0, /* 225 */ - 0, /* 226 */ - 0, /* 227 */ - 0, /* 228 */ - 0, /* 229 */ - 0, /* 230 */ - 0, /* 231 */ - 0, /* 232 */ - 0, /* 233 */ - 0, /* 234 */ - 0, /* 235 */ - 0, /* 236 */ - 0, /* 237 */ - 0, /* 238 */ - 0, /* 239 */ - 0, /* 240 */ - 0, /* 241 */ - 0, /* 242 */ - 0, /* 243 */ - 0, /* 244 */ - 0, /* 245 */ - 0, /* 246 */ - 0, /* 247 */ - 0, /* 248 */ - 0, /* 249 */ - 0, /* 250 */ - 0, /* 251 */ - 0, /* 252 */ - 0, /* 253 */ - 0, /* 254 */ - 0, /* 255 */ - }; - - internal enum UDTFormatType - { - Native = 1, - UserDefined = 2 - } - - internal enum TransactionManagerRequestType - { - GetDTCAddress = 0, - Propagate = 1, - Begin = 5, - Promote = 6, - Commit = 7, - Rollback = 8, - Save = 9 - }; - - internal enum TransactionManagerIsolationLevel - { - Unspecified = 0x00, - ReadUncommitted = 0x01, - ReadCommitted = 0x02, - RepeatableRead = 0x03, - Serializable = 0x04, - Snapshot = 0x05 - } - - internal enum GenericType - { - MultiSet = 131, - }; - - // Date, Time, DateTime2, DateTimeOffset specific constants - internal static readonly Int64[] TICKS_FROM_SCALE = { - 10000000, - 1000000, - 100000, - 10000, - 1000, - 100, - 10, - 1, - }; - - internal const int MAX_TIME_SCALE = 7; // Maximum scale for time-related types - internal const int MAX_TIME_LENGTH = 5; // Maximum length for time - internal const int MAX_DATETIME2_LENGTH = 8; // Maximum length for datetime2 - internal const int WHIDBEY_DATE_LENGTH = 10; - internal static readonly int[] WHIDBEY_TIME_LENGTH = { 8, 10, 11, 12, 13, 14, 15, 16 }; - internal static readonly int[] WHIDBEY_DATETIME2_LENGTH = { 19, 21, 22, 23, 24, 25, 26, 27 }; - internal static readonly int[] WHIDBEY_DATETIMEOFFSET_LENGTH = { 26, 28, 29, 30, 31, 32, 33, 34 }; - - internal enum FedAuthInfoId : byte - { - Stsurl = 0x01, // FedAuthInfoData is token endpoint URL from which to acquire fed auth token - Spn = 0x02, // FedAuthInfoData is the SPN to use for acquiring fed auth token - } - - // Data Classification constants - internal const byte DATA_CLASSIFICATION_NOT_ENABLED = 0x00; - internal const byte DATA_CLASSIFICATION_VERSION_WITHOUT_RANK_SUPPORT = 0x01; - internal const byte DATA_CLASSIFICATION_VERSION_MAX_SUPPORTED = 0x02; - - // TCE Related constants - internal const byte MAX_SUPPORTED_TCE_VERSION = 0x03; // max version - internal const byte MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT = 0x02; // min version with enclave support - internal const ushort MAX_TCE_CIPHERINFO_SIZE = 2048; // max size of cipherinfo blob - internal const long MAX_TCE_CIPHERTEXT_SIZE = 2147483648; // max size of encrypted blob- currently 2GB. - internal const byte CustomCipherAlgorithmId = 0; // Id used for custom encryption algorithm. - - internal const int AEAD_AES_256_CBC_HMAC_SHA256 = 2; - internal const string ENCLAVE_TYPE_VBS = "VBS"; - internal const string ENCLAVE_TYPE_SGX = "SGX"; -#if ENCLAVE_SIMULATOR - internal const string ENCLAVE_TYPE_SIMULATOR = "SIMULATOR"; -#endif - // TCE Param names for exec handling - internal const string TCE_PARAM_CIPHERTEXT = "cipherText"; - internal const string TCE_PARAM_CIPHER_ALGORITHM_ID = "cipherAlgorithmId"; - internal const string TCE_PARAM_COLUMNENCRYPTION_KEY = "columnEncryptionKey"; - internal const string TCE_PARAM_ENCRYPTION_ALGORITHM = "encryptionAlgorithm"; - internal const string TCE_PARAM_ENCRYPTIONTYPE = "encryptionType"; - internal const string TCE_PARAM_ENCRYPTIONKEY = "encryptionKey"; - internal const string TCE_PARAM_MASTERKEY_PATH = "masterKeyPath"; - internal const string TCE_PARAM_ENCRYPTED_CEK = "encryptedColumnEncryptionKey"; - internal const string TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS = "clientKeyStoreProviders"; - internal const string TCE_PARAM_FORCE_COLUMN_ENCRYPTION = "ForceColumnEncryption(true)"; - } - - internal enum ParsingErrorState - { - Undefined = 0, - FedAuthInfoLengthTooShortForCountOfInfoIds = 1, - FedAuthInfoLengthTooShortForData = 2, - FedAuthInfoFailedToReadCountOfInfoIds = 3, - FedAuthInfoFailedToReadTokenStream = 4, - FedAuthInfoInvalidOffset = 5, - FedAuthInfoFailedToReadData = 6, - FedAuthInfoDataNotUnicode = 7, - FedAuthInfoDoesNotContainStsurlAndSpn = 8, - FedAuthInfoNotReceived = 9, - FedAuthNotAcknowledged = 10, - FedAuthFeatureAckContainsExtraData = 11, - FedAuthFeatureAckUnknownLibraryType = 12, - UnrequestedFeatureAckReceived = 13, - UnknownFeatureAck = 14, - InvalidTdsTokenReceived = 15, - SessionStateLengthTooShort = 16, - SessionStateInvalidStatus = 17, - CorruptedTdsStream = 18, - ProcessSniPacketFailed = 19, - FedAuthRequiredPreLoginResponseInvalidValue = 20, - TceUnknownVersion = 21, - TceInvalidVersion = 22, - TceInvalidOrdinalIntoCipherInfoTable = 23, - DataClassificationInvalidVersion = 24, - DataClassificationNotExpected = 25, - DataClassificationInvalidLabelIndex = 26, - DataClassificationInvalidInformationTypeIndex = 27 - } - - internal enum SniContext - { - Undefined = 0, - Snix_Connect, - Snix_PreLoginBeforeSuccessfullWrite, - Snix_PreLogin, - Snix_LoginSspi, - Snix_ProcessSspi, - Snix_Login, - Snix_EnableMars, - Snix_AutoEnlist, - Snix_GetMarsSession, - Snix_Execute, - Snix_Read, - Snix_Close, - Snix_SendRows, - } - - /// - public enum SqlConnectionColumnEncryptionSetting - { - /// - Disabled = 0, - - /// - Enabled, - } - - /// - [Flags] - public enum SqlConnectionOverrides - { - /// - None = 0, - /// - OpenWithoutRetry = 1, - } - - /// - public enum SqlCommandColumnEncryptionSetting - { - /// - UseConnectionSetting = 0, - - /// - Enabled, - - /// - ResultSetOnly, - - /// - Disabled, - } - - /// - public enum SqlConnectionAttestationProtocol - { - /// - NotSpecified = 0, - - /// - AAS = 1, - -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif - - /// - HGS = 3 - } - - /// - public enum SqlConnectionIPAddressPreference - { - /// - IPv4First = 0, // default - - /// - IPv6First = 1, - - /// - UsePlatformDefault = 2 - } - - /// - public enum SqlAuthenticationMethod - { - /// - NotSpecified = 0, - - /// - SqlPassword, - - /// - ActiveDirectoryPassword, - - /// - ActiveDirectoryIntegrated, - - /// - ActiveDirectoryInteractive, - - /// - ActiveDirectoryServicePrincipal, - - /// - ActiveDirectoryDeviceCodeFlow, - - /// - ActiveDirectoryManagedIdentity, - - /// - ActiveDirectoryMSI, - - /// - ActiveDirectoryDefault, -#if ADONET_CERT_AUTH - SqlCertificate, -#endif - /// - /// - /// - ActiveDirectoryTokenCredential - } - // This enum indicates the state of TransparentNetworkIPResolution - // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. - internal enum TransparentNetworkResolutionState - { - DisabledMode = 0, - SequentialMode, - ParallelMode - }; - - internal class ActiveDirectoryAuthentication - { - internal const string AdoClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; - internal const string MSALGetAccessTokenFunctionName = "AcquireToken"; - } - - // Fields in the first resultset of "sp_describe_parameter_encryption". - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet1 - { - KeyOrdinal = 0, - DbId, - KeyId, - KeyVersion, - KeyMdVersion, - EncryptedKey, - ProviderName, - KeyPath, - KeyEncryptionAlgorithm, - IsRequestedByEnclave, - KeySignature, - } - - // Fields in the second resultset of "sp_describe_parameter_encryption" - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet2 - { - ParameterOrdinal = 0, - ParameterName, - ColumnEncryptionAlgorithm, - ColumnEncryptionType, - ColumnEncryptionKeyOrdinal, - NormalizationRuleVersion, - } - - // Fields in the third resultset of "sp_describe_parameter_encryption". - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet3 - { - AttestationInfo = 0, - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index b30f54f8e5..e4db5b96d2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -39,7 +39,7 @@ internal Strings() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Data.SqlClient.Resources.Strings", typeof(Strings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SqlClient.Resources.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; @@ -906,6 +906,15 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenAndIntegratedSecurity { } } + /// + /// Looks up a localized string similar to Cannot set the AccessToken property if the AccessTokenCallback has been set.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. /// @@ -915,6 +924,24 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenAndUserIDPassword { } } + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. + /// + internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the Credential property if the AccessToken property is already set.. /// @@ -924,6 +951,15 @@ internal static string ADP_InvalidMixedUsageOfCredentialAndAccessToken { } } + /// + /// Looks up a localized string similar to Cannot set the Credential property if the AccessTokenCallback property is already set.. + /// + internal static string ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot use Credential with UserID, UID, Password, or PWD connection string keywords.. /// @@ -5559,6 +5595,15 @@ internal static string DbConnectionString_ApplicationName { } } + /// + /// Looks up a localized string similar to When true, enables usage of the Asynchronous functionality in the .NET Framework Data Provider.. + /// + internal static string DbConnectionString_AsynchronousProcessing { + get { + return ResourceManager.GetString("DbConnectionString_AsynchronousProcessing", resourceCulture); + } + } + /// /// Looks up a localized string similar to The name of the primary file, including the full path name, of an attachable database.. /// @@ -8910,6 +8955,15 @@ internal static string SQL_ArgumentLengthMismatch { } } + /// + /// Looks up a localized string similar to This command requires an asynchronous connection. Set "Asynchronous Processing=true" in the connection string.. + /// + internal static string SQL_AsyncConnectionRequired { + get { + return ResourceManager.GetString("SQL_AsyncConnectionRequired", resourceCulture); + } + } + /// /// Looks up a localized string similar to The asynchronous operation has already completed.. /// @@ -10593,6 +10647,15 @@ internal static string SqlConnection_AccessToken { } } + /// + /// Looks up a localized string similar to State of connection, synchronous or asynchronous. 'Asynchronous Processing=x' in the connection string.. + /// + internal static string SqlConnection_Asynchronous { + get { + return ResourceManager.GetString("SqlConnection_Asynchronous", resourceCulture); + } + } + /// /// Looks up a localized string similar to A guid to represent the physical connection.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 85627eae31..db82b9650a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4635,4 +4635,16 @@ The '{0}' platform is not supported when targeting .NET Framework. - + + Cannot set the AccessToken property if the AccessTokenCallback has been set. + + + Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. + + + Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. + + + Cannot set the Credential property if the AccessTokenCallback property is already set. + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs index 9b9c0f3341..abeadf86b7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index d83db5ab74..0a9dc5bf47 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -1268,7 +1268,22 @@ static internal InvalidOperationException InvalidMixedUsageOfAccessTokenAndAuthe static internal Exception InvalidMixedUsageOfCredentialAndAccessToken() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessToken)); -#endregion + + static internal Exception InvalidMixedUsageOfAccessTokenAndTokenCallback() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback)); + + internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndAuthentication() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback)); + + internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity)); + + internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword)); + + internal static Exception InvalidMixedUsageOfCredentialAndAccessTokenCallback() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback)); + #endregion internal static bool IsEmpty(string str) => string.IsNullOrEmpty(str); internal static readonly IntPtr s_ptrZero = IntPtr.Zero; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 19e54b612e..f8a31231c0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -361,8 +361,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj "ActiveDirectoryDeviceCodeFlow", "ActiveDirectoryManagedIdentity", "ActiveDirectoryMSI", - "ActiveDirectoryDefault", - "ActiveDirectoryTokenCredential" + "ActiveDirectoryDefault" }; private static bool IsValidAuthenticationMethodEnum() @@ -517,7 +516,7 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 11, "SqlAuthenticationMethod enum has changed, update needed"); + Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed"); return value == SqlAuthenticationMethod.SqlPassword || value == SqlAuthenticationMethod.ActiveDirectoryPassword || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs index 26b8ecabaf..a39ca72584 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -285,7 +285,6 @@ public enum ActiveDirectoryWorkflow : byte DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, - TokenCredential = MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. From 9a9cd870adfab76eb4ffbed9a767b796181fe666 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 18 Apr 2023 15:04:38 -0500 Subject: [PATCH 17/33] tests --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 5 ++++ .../netcore/src/Resources/Strings.Designer.cs | 9 ++++++++ .../netcore/src/Resources/Strings.resx | 3 +++ .../netfx/src/Resources/Strings.Designer.cs | 9 ++++++++ .../netfx/src/Resources/Strings.resx | 3 +++ .../src/Microsoft/Data/Common/AdapterUtil.cs | 3 +++ .../ConnectivityTests/AADConnectionTest.cs | 23 +++++++++++++++++++ 7 files changed, 55 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 0f8fd52256..dc2636011f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1082,6 +1082,11 @@ private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCa throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity(); } + if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw ADP.InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback(); + } + if (UsesAuthentication(connectionOptions)) { throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndAuthentication(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index ea913abea4..6ded431a33 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -465,6 +465,15 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPasswo } } + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. + /// + internal static string ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the Credential property if the AccessToken property is already set.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 9d164fab39..5d81d2fcdf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1950,6 +1950,9 @@ Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. + + Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. + Cannot set the Credential property if the AccessTokenCallback property is already set. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index e4db5b96d2..e1f08b3407 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -942,6 +942,15 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPasswo } } + /// + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. + /// + internal static string ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback { + get { + return ResourceManager.GetString("ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set the Credential property if the AccessToken property is already set.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index db82b9650a..e1ea45c854 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4644,6 +4644,9 @@ Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. + + Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. + Cannot set the Credential property if the AccessTokenCallback property is already set. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index 0a9dc5bf47..d75877f3a2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -1283,6 +1283,9 @@ internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndUserIDPasswor internal static Exception InvalidMixedUsageOfCredentialAndAccessTokenCallback() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback)); + + internal static Exception InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback() + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback)); #endregion internal static bool IsEmpty(string str) => string.IsNullOrEmpty(str); 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 70cae84d73..98583ebf13 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -551,6 +551,29 @@ public static void ActiveDirectoryDefaultWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } + [ConditionalFact(nameof(IsAADConnStringsSetup))] + 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"; + InvalidOperationException e = Assert.Throws(() => + { + using (SqlConnection conn = new SqlConnection(connStrWithNoCred)) + { + conn.AccessTokenCallback = (ctx, token) => + Task.FromResult(new SqlAuthenticationToken("my token", DateTimeOffset.MaxValue)); + conn.Open(); + + Assert.True(conn.State == System.Data.ConnectionState.Open); + } + }); + + 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))] public static void ActiveDirectoryDefaultMustPass() { From 677f7293e863891205ad64758e62ad08dbca0e91 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 19 Apr 2023 09:36:47 -0500 Subject: [PATCH 18/33] fix resources --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 13 ++--------- .../netfx/src/Resources/Strings.Designer.cs | 2 +- .../ConnectivityTests/AADConnectionTest.cs | 22 +++++++++++++++++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 69bea3505f..463f47fee6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -690,16 +690,7 @@ ResXFileCodeGenerator $(ResxFileName).Designer.cs - - - - - - - - - - + Microsoft.Data.SqlClient.SqlMetaData.xml PreserveNewest @@ -747,4 +738,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index e1f08b3407..0783760486 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -39,7 +39,7 @@ internal Strings() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SqlClient.Resources.Strings", typeof(Strings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Data.SqlClient.Resources.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; 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 98583ebf13..786f3ee913 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,8 @@ using System.Security; using System.Threading; using System.Threading.Tasks; +using Azure.Core; +using Azure.Identity; using Microsoft.Identity.Client; using Xunit; @@ -576,6 +578,26 @@ public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() [ConditionalFact(nameof(IsAADConnStringsSetup))] public static void ActiveDirectoryDefaultMustPass() + { + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); + + var cred = new DefaultAzureCredential(); + 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; + var token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); + return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); + }; + conn.Open(); + } + } + + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void AccessTokenCallbackDefaultMustPass() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + From afc5fff8146b1f2fbf1351325ab75f0cccbf6cf8 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 19 Apr 2023 11:00:59 -0500 Subject: [PATCH 19/33] fix error messages for fx --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 1c97be97a3..8e8e22a829 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -759,7 +759,7 @@ public Func Date: Wed, 19 Apr 2023 15:30:24 -0500 Subject: [PATCH 20/33] fixes --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 65 +++++++++++++------ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 13 ++-- .../Data/SqlClient/SqlConnectionPoolKey.cs | 2 +- .../ConnectivityTests/AADConnectionTest.cs | 5 +- 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index dc2636011f..08f6d8ae39 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -718,7 +718,7 @@ public Func> accessTokenCallback = null) : base(connectionOptions) + Func> accessTokenCallback = null) : base(connectionOptions) { #if DEBUG @@ -2383,7 +2384,6 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) databaseName: ConnectionOptions.InitialCatalog) .WithConnectionId(_clientConnectionId) .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); - switch (ConnectionOptions.Authentication) { case SqlAuthenticationMethod.ActiveDirectoryIntegrated: diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 8e8e22a829..2396e5d67c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -804,7 +804,7 @@ override public string ConnectionString } set { - if (_credential != null || _accessToken != null) + if (_credential != null || _accessToken != null || _accessTokenCallback != null) { SqlConnectionString connectionOptions = new SqlConnectionString(value); if (_credential != null) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 86d37a55dc..114f4a7908 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -434,7 +434,8 @@ internal SqlInternalConnectionTds( string accessToken = null, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo = null, bool applyTransientFaultHandling = false, - Func> accessTokenCallback = null) : base(connectionOptions) + Func> accessTokenCallback = null) : base(connectionOptions) { #if DEBUG @@ -2596,6 +2597,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) || _credential != null + || _accessTokenCallback != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI @@ -2790,13 +2792,13 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) int numberOfAttempts = 0; // Object that will be returned to the caller, containing all required data about the token. - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(); + _fedAuthToken = new SqlFedAuthToken(); // Username to use in error messages. string username = null; var authProvider = _sqlAuthenticationProviderManager.GetProvider(ConnectionOptions.Authentication); - if (authProvider == null) + if (authProvider == null && _accessTokenCallback == null) throw SQL.CannotFindAuthProvider(ConnectionOptions.Authentication.ToString()); // retry getting access token once if MsalException.error_code is unknown_error. @@ -2820,14 +2822,14 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) username = TdsEnums.NTAUTHORITYANONYMOUSLOGON; if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { - fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; } else { // We use Task.Run here in all places to execute task synchronously in the same context. // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 - fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; case SqlAuthenticationMethod.ActiveDirectoryInteractive: @@ -2837,20 +2839,20 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { - fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; } else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); - fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; case SqlAuthenticationMethod.ActiveDirectoryPassword: case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { - fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; } else { @@ -2858,22 +2860,44 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { username = _credential.UserId; authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } else { username = ConnectionOptions.UserID; authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } - _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; default: - throw SQL.UnsupportedAuthenticationSpecified(ConnectionOptions.Authentication); + if (_accessTokenCallback == null) + { + throw SQL.UnsupportedAuthenticationSpecified(ConnectionOptions.Authentication); + } + + if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) + { + _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; + } + else + { + authParamsBuilder.WithUserId(ConnectionOptions.UserID); + SqlAuthenticationParameters parameters = authParamsBuilder; + CancellationTokenSource cts = new(); + // Use Connection timeout value to cancel token acquire request after certain period of time.(int) + if (_timeout.MillisecondsRemaining < Int32.MaxValue) + { + cts.CancelAfter((int)_timeout.MillisecondsRemaining); + } + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; + } + break; } - Debug.Assert(fedAuthToken.accessToken != null, "AccessToken should not be null."); + Debug.Assert(_fedAuthToken.accessToken != null, "AccessToken should not be null."); #if DEBUG if (_forceMsalRetry) { @@ -2941,17 +2965,17 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } } - Debug.Assert(fedAuthToken != null, "fedAuthToken should not be null."); - Debug.Assert(fedAuthToken.accessToken != null && fedAuthToken.accessToken.Length > 0, "fedAuthToken.accessToken should not be null or empty."); + Debug.Assert(_fedAuthToken != null, "fedAuthToken should not be null."); + Debug.Assert(_fedAuthToken.accessToken != null && _fedAuthToken.accessToken.Length > 0, "fedAuthToken.accessToken should not be null or empty."); // Store the newly generated token in _newDbConnectionPoolAuthenticationContext, only if using pooling. if (_dbConnectionPool != null) { - DateTime expirationTime = DateTime.FromFileTimeUtc(fedAuthToken.expirationFileTime); - _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(fedAuthToken.accessToken, expirationTime); + DateTime expirationTime = DateTime.FromFileTimeUtc(_fedAuthToken.expirationFileTime); + _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(_fedAuthToken.accessToken, expirationTime); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Finished generating federated authentication token.", ObjectID); - return fedAuthToken; + return _fedAuthToken; } internal void OnFeatureExtAck(int featureId, byte[] data) @@ -3029,6 +3053,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) switch (_fedAuthFeatureExtensionData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: case TdsEnums.FedAuthLibrary.SecurityToken: // The server shouldn't have sent any additional data with the ack (like a nonce) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 605a0b9ee8..fdbabe8bf0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1487,7 +1487,10 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( } // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || (_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null)) && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || + ((authType != SqlAuthenticationMethod.NotSpecified || (_connHandler._accessTokenInBytes != null || + _connHandler._accessTokenCallback != null)) + && !trustServerCert); UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); @@ -8688,6 +8691,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD // set upper 7 bits of options to indicate fed auth library type switch (fedAuthFeatureData.libraryType) { + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: case TdsEnums.FedAuthLibrary.MSAL: Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_MSAL << 1; @@ -8696,10 +8700,6 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD Debug.Assert(_connHandler._federatedAuthenticationRequested == true, "_federatedAuthenticationRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_SECURITYTOKEN << 1; break; - case TdsEnums.FedAuthLibrary.SecurityTokenCallback: - Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); - options |= TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL << 1; - break; default: Debug.Fail("Unrecognized FedAuthLibrary type for feature extension request"); break; @@ -8752,6 +8752,9 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD WriteInt(fedAuthFeatureData.accessToken.Length, _physicalStateObj); _physicalStateObj.WriteByteArray(fedAuthFeatureData.accessToken, fedAuthFeatureData.accessToken.Length, 0); break; + case TdsEnums.FedAuthLibrary.SecurityTokenCallback: + _physicalStateObj.WriteByte(TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYTOKENCREDENTIAL); + break; default: Debug.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request"); break; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index 29d966ba6a..47aa54e1cd 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -56,7 +56,7 @@ internal SqlConnectionPoolKey(string connectionString, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, Func> accessTokenCallback = null) : base(connectionString) { - Debug.Assert(_credential == null || _accessToken == null, "Credential and AccessToken can't have the value at the same time."); + Debug.Assert(_credential == null || _accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; _accessToken = accessToken; _accessTokenCallback = accessTokenCallback; 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 786f3ee913..f13ebcbec8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -577,11 +577,10 @@ public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() } [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void ActiveDirectoryDefaultMustPass() + public static void AccessTokenCallbackDefaultMustPass() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); - var cred = new DefaultAzureCredential(); const string defaultScopeSuffix = "/.default"; using (SqlConnection conn = new SqlConnection(connStr)) @@ -597,7 +596,7 @@ public static void ActiveDirectoryDefaultMustPass() } [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void AccessTokenCallbackDefaultMustPass() + public static void ActiveDirectoryDefaultMustPass() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + From b08a251fed41e8c21bef754ec0b1e5574bf3e8a4 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Thu, 20 Apr 2023 16:45:18 -0500 Subject: [PATCH 21/33] rename AzureADTokenRequestContext --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 2 +- ...kenRequestContext.cs => AzureADTokenRequestContext.cs} | 4 ++-- .../netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ++-- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 6 +++--- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 ++-- .../netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ++-- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 6 +++--- .../src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs | 8 ++++---- 8 files changed, 19 insertions(+), 19 deletions(-) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{AadTokenRequestContext.cs => AzureADTokenRequestContext.cs} (80%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index acb42a2ac9..350f72c4e3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -972,7 +972,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs similarity index 80% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs index ce242dbe72..a37915e8c0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AadTokenRequestContext.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs @@ -9,13 +9,13 @@ namespace Microsoft.Data.SqlClient /// /// /// - public class AadTokenRequestContext + public class AzureADTokenRequestContext { /// /// /// /// - public AadTokenRequestContext(string resource) { Resource = resource; } + public AzureADTokenRequestContext(string resource) { Resource = resource; } /// /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 08f6d8ae39..b215b104f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -89,7 +89,7 @@ private static readonly Dictionary /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -697,7 +697,7 @@ public string AccessToken /// /// /// - public Func> AccessTokenCallback + public Func> AccessTokenCallback { get { return _accessTokenCallback; } set diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 92992f9eb7..8255c136b3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -130,7 +130,7 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; - internal readonly Func> _accessTokenCallback; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -447,7 +447,7 @@ internal SqlInternalConnectionTds( bool applyTransientFaultHandling = false, string accessToken = null, DbConnectionPool pool = null, - Func> accessTokenCallback = null) : base(connectionOptions) { @@ -2470,7 +2470,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { cts.CancelAfter((int)_timeout.MillisecondsRemaining); } - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AzureADTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 463f47fee6..a99c4aeb84 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -89,8 +89,8 @@ - - Microsoft\Data\SqlClient\AadTokenRequestContext.cs + + Microsoft\Data\SqlClient\AzureADTokenRequestContext.cs Microsoft\Data\Common\ActivityCorrelator.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 2396e5d67c..e91d1febbd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -72,7 +72,7 @@ private static Dictionary s_systemC /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -745,7 +745,7 @@ public string AccessToken /// /// /// - public Func> AccessTokenCallback + public Func> AccessTokenCallback { get { return _accessTokenCallback; } set diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 114f4a7908..a47da4296d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -136,7 +136,7 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; - internal readonly Func> _accessTokenCallback; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -434,7 +434,7 @@ internal SqlInternalConnectionTds( string accessToken = null, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo = null, bool applyTransientFaultHandling = false, - Func> accessTokenCallback = null) : base(connectionOptions) { @@ -2891,7 +2891,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { cts.CancelAfter((int)_timeout.MillisecondsRemaining); } - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AadTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AzureADTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index 47aa54e1cd..a7967cf913 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -17,11 +17,11 @@ internal class SqlConnectionPoolKey : DbConnectionPoolKey private int _hashValue; private readonly SqlCredential _credential; private readonly string _accessToken; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal SqlCredential Credential => _credential; internal string AccessToken => _accessToken; - internal Func> AccessTokenCallback => _accessTokenCallback; + internal Func> AccessTokenCallback => _accessTokenCallback; internal override string ConnectionString { @@ -54,7 +54,7 @@ internal SqlConnectionPoolKey(string connectionString, ServerCertificateValidationCallback serverCertificateValidationCallback, ClientCertificateRetrievalCallback clientCertificateRetrievalCallback, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, - Func> accessTokenCallback = null) : base(connectionString) + Func> accessTokenCallback = null) : base(connectionString) { Debug.Assert(_credential == null || _accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; @@ -68,7 +68,7 @@ internal SqlConnectionPoolKey(string connectionString, #endregion #else #region NET Core - internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) + internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) { Debug.Assert(credential == null || accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; From be1263e9030fa60c35e51137c5039ab2cacd6cd8 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 2 May 2023 15:59:16 -0500 Subject: [PATCH 22/33] use SqlAuthenticationParameters in callback --- .../src/Microsoft.Data.SqlClient.csproj | 3 --- .../SqlClient/AzureADTokenRequestContext.cs | 25 ------------------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +-- .../SqlClient/SqlInternalConnectionTds.cs | 6 ++--- .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 +--- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +-- .../SqlClient/SqlInternalConnectionTds.cs | 6 ++--- .../Data/SqlClient/SqlConnectionPoolKey.cs | 8 +++--- 8 files changed, 15 insertions(+), 46 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 350f72c4e3..bc755d3486 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -971,9 +971,6 @@ - - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs deleted file mode 100644 index a37915e8c0..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureADTokenRequestContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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.Threading; - -namespace Microsoft.Data.SqlClient -{ - /// - /// - /// - public class AzureADTokenRequestContext - { - /// - /// - /// - /// - public AzureADTokenRequestContext(string resource) { Resource = resource; } - - /// - /// - /// - public string Resource { get; } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index b215b104f8..cedd218a5e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -89,7 +89,7 @@ private static readonly Dictionary /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -697,7 +697,7 @@ public string AccessToken /// /// /// - public Func> AccessTokenCallback + public Func> AccessTokenCallback { get { return _accessTokenCallback; } set diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 8255c136b3..61f8ed6d93 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -130,7 +130,7 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; - internal readonly Func> _accessTokenCallback; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -447,7 +447,7 @@ internal SqlInternalConnectionTds( bool applyTransientFaultHandling = false, string accessToken = null, DbConnectionPool pool = null, - Func> accessTokenCallback = null) : base(connectionOptions) { @@ -2470,7 +2470,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { cts.CancelAfter((int)_timeout.MillisecondsRemaining); } - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AzureADTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(parameters, cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index a99c4aeb84..ebb004e153 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -89,9 +89,6 @@ - - Microsoft\Data\SqlClient\AzureADTokenRequestContext.cs - Microsoft\Data\Common\ActivityCorrelator.cs @@ -738,4 +735,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index e91d1febbd..199a6e344c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -72,7 +72,7 @@ private static Dictionary s_systemC /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal bool HasColumnEncryptionKeyStoreProvidersRegistered => _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; @@ -745,7 +745,7 @@ public string AccessToken /// /// /// - public Func> AccessTokenCallback + public Func> AccessTokenCallback { get { return _accessTokenCallback; } set diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index a47da4296d..c6858f72a8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -136,7 +136,7 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; - internal readonly Func> _accessTokenCallback; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; @@ -434,7 +434,7 @@ internal SqlInternalConnectionTds( string accessToken = null, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo = null, bool applyTransientFaultHandling = false, - Func> accessTokenCallback = null) : base(connectionOptions) { @@ -2891,7 +2891,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { cts.CancelAfter((int)_timeout.MillisecondsRemaining); } - _fedAuthToken = Task.Run(async () => await _accessTokenCallback(new AzureADTokenRequestContext(parameters.Resource), cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); + _fedAuthToken = Task.Run(async () => await _accessTokenCallback(parameters, cts.Token)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index a7967cf913..21143cf630 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -17,11 +17,11 @@ internal class SqlConnectionPoolKey : DbConnectionPoolKey private int _hashValue; private readonly SqlCredential _credential; private readonly string _accessToken; - private Func> _accessTokenCallback; + private Func> _accessTokenCallback; internal SqlCredential Credential => _credential; internal string AccessToken => _accessToken; - internal Func> AccessTokenCallback => _accessTokenCallback; + internal Func> AccessTokenCallback => _accessTokenCallback; internal override string ConnectionString { @@ -54,7 +54,7 @@ internal SqlConnectionPoolKey(string connectionString, ServerCertificateValidationCallback serverCertificateValidationCallback, ClientCertificateRetrievalCallback clientCertificateRetrievalCallback, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, - Func> accessTokenCallback = null) : base(connectionString) + Func> accessTokenCallback = null) : base(connectionString) { Debug.Assert(_credential == null || _accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; @@ -68,7 +68,7 @@ internal SqlConnectionPoolKey(string connectionString, #endregion #else #region NET Core - internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) + internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) { Debug.Assert(credential == null || accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); _credential = credential; From 52a84f93b334266fe861c3ddc0efc9eaf3a4d37d Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 2 May 2023 17:07:46 -0500 Subject: [PATCH 23/33] docs and simple sample --- .../SqlConnection_AccessTokenCallback.cs | 34 +++++++++++++++++++ .../SqlConnection.xml | 15 ++++++++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +-- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +-- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 doc/samples/SqlConnection_AccessTokenCallback.cs diff --git a/doc/samples/SqlConnection_AccessTokenCallback.cs b/doc/samples/SqlConnection_AccessTokenCallback.cs new file mode 100644 index 0000000000..a195e48585 --- /dev/null +++ b/doc/samples/SqlConnection_AccessTokenCallback.cs @@ -0,0 +1,34 @@ +using System; +using System.Data; +// +using Microsoft.Data.SqlClient; +using Azure.Identity; + +class Program +{ + static void Main() + { + OpenSqlConnection(); + Console.ReadLine(); + } + + private static void OpenSqlConnection() + { + string connectionString = GetConnectionString(); + using (SqlConnection connection = new SqlConnection("Data Source=contoso.database.windows.net;Initial Catalog=AdventureWorks;") + { + AccessTokenCallback = async (authParams, cancellationToken) => + { + var cred = new DefaultAzureCredential(); + string scope = authParams.Resource.EndsWith(s_defaultScopeSuffix) ? authParams.Resource : authParams.Resource + s_defaultScopeSuffix; + var token = await cred.GetTokenAsync(new TokenRequestContext(new[] { scope }), cancellationToken); + return new SqlAuthenticationToken(token.Token, token.ExpiresOn); + } + }) + { + connection.Open(); + Console.WriteLine("ServerVersion: {0}", connection.ServerVersion); + Console.WriteLine("State: {0}", connection.State); + } + } +// diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index bfc53dddac..001e47c38f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -129,6 +129,21 @@ using (SqlConnection connection = new SqlConnection(connectionString)) The access token for the connection. To be added. + + Gets or sets the access token callback for the connection. + + The Func that takes a and and returns a . + + . + + [!code-csharp[SqlConnection_AccessTokenCallback Example#1](~/../sqlclient/doc/samples/SqlConnection_AccessTokenCallback.cs#1)] + + ]]> + + To be added. To be added. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index cedd218a5e..59fb0cb849 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -694,9 +694,7 @@ public string AccessToken } } - /// - /// - /// + /// public Func> AccessTokenCallback { get { return _accessTokenCallback; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 199a6e344c..ca0185089e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -742,9 +742,7 @@ public string AccessToken } } - /// - /// - /// + /// public Func> AccessTokenCallback { get { return _accessTokenCallback; } From 9be9391628aa5f4f80a13f935b61b0e162c2c352 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 3 May 2023 10:25:26 -0500 Subject: [PATCH 24/33] tests for password and userId with callback --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 5 ---- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../Microsoft/Data/SqlClient/SqlConnection.cs | 5 ---- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../ConnectivityTests/AADConnectionTest.cs | 26 ++++++++++++++++++- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 59fb0cb849..c86d3442c5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1070,11 +1070,6 @@ private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(S // This is to be used setter of ConnectionString and AccessTokenCallback properties private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(SqlConnectionString connectionOptions) { - if (UsesClearUserIdOrPassword(connectionOptions)) - { - throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword(); - } - if (UsesIntegratedSecurity(connectionOptions)) { throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 61f8ed6d93..7510b0cb18 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2463,6 +2463,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); + authParamsBuilder.WithPassword(ConnectionOptions.Password); SqlAuthenticationParameters parameters = authParamsBuilder; CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time.(int) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index ca0185089e..53acdd0eae 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1264,11 +1264,6 @@ private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(S // This is to be used setter of ConnectionString and AccessTokenCallback properties private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(SqlConnectionString connectionOptions) { - if (UsesClearUserIdOrPassword(connectionOptions)) - { - throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword(); - } - if (UsesIntegratedSecurity(connectionOptions)) { throw ADP.InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index c6858f72a8..828539c43c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2884,6 +2884,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); + authParamsBuilder.WithPassword(ConnectionOptions.Password); SqlAuthenticationParameters parameters = authParamsBuilder; CancellationTokenSource cts = new(); // Use Connection timeout value to cancel token acquire request after certain period of time.(int) 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 f13ebcbec8..e745309ccf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -577,7 +577,7 @@ public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() } [ConditionalFact(nameof(IsAADConnStringsSetup))] - public static void AccessTokenCallbackDefaultMustPass() + public static void AccessTokenCallbackMustPass() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); @@ -595,6 +595,30 @@ public static void AccessTokenCallbackDefaultMustPass() } } + [ConditionalFact(nameof(IsAADConnStringsSetup))] + 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 = new DefaultAzureCredential(); + const string defaultScopeSuffix = "/.default"; + using (SqlConnection conn = new SqlConnection(connStr)) + { + 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; + var token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); + return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); + }; + conn.Open(); + } + } + [ConditionalFact(nameof(IsAADConnStringsSetup))] public static void ActiveDirectoryDefaultMustPass() { From a2233f5f6a02dfd1c735e02e572eb12a1c1d2658 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 3 May 2023 10:38:11 -0500 Subject: [PATCH 25/33] add to SqlClient.cs API listing --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 6 ++++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 58dfd74e1e..ef6a3e254f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -4,6 +4,10 @@ // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. // New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. +using System.Threading.Tasks; +using System.Threading; +using System; + [assembly: System.CLSCompliant(true)] namespace Microsoft.Data { @@ -837,6 +841,8 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collect /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public System.Guid ClientConnectionId { get { throw null; } } + /// + public Func> AccessTokenCallback { get { throw null; } set { } } /// /// for internal test only diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 2a8197448c..a5d2be8022 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -4,6 +4,10 @@ // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. // New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. +using System.Threading.Tasks; +using System.Threading; +using System; + [assembly: System.CLSCompliant(true)] [assembly: System.Resources.NeutralResourcesLanguageAttribute("en-US")] namespace Microsoft.Data @@ -765,6 +769,8 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public string AccessToken { get { throw null; } set { } } + /// + public Func> AccessTokenCallback { get { throw null; } set { } } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public System.Guid ClientConnectionId { get { throw null; } } From c1df1f4793a1a9d50e3dee594b44fc14f891b220 Mon Sep 17 00:00:00 2001 From: David Engel Date: Fri, 12 May 2023 16:58:19 -0700 Subject: [PATCH 26/33] Allow credential with callback and pass to the callback, if available Map to the correct FEDAUTHLIB_MSAL value Add functional tests for connection options Other minor adjustments --- .../SqlConnection_AccessTokenCallback.cs | 13 +-- .../netcore/ref/Microsoft.Data.SqlClient.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 32 +++----- .../SqlClient/SqlInternalConnectionTds.cs | 28 +++---- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 16 ++-- .../netcore/src/Resources/Strings.Designer.cs | 24 +----- .../netcore/src/Resources/Strings.resx | 10 +-- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 33 +++----- .../SqlClient/SqlInternalConnectionTds.cs | 28 +++---- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 16 ++-- .../netfx/src/Resources/Strings.Designer.cs | 24 +----- .../netfx/src/Resources/Strings.resx | 10 +-- .../src/Microsoft/Data/Common/AdapterUtil.cs | 11 +-- .../Data/SqlClient/SqlConnectionPoolKey.cs | 6 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 4 +- .../SqlConnectionBasicTests.cs | 81 +++++++++++++++++++ 17 files changed, 173 insertions(+), 171 deletions(-) diff --git a/doc/samples/SqlConnection_AccessTokenCallback.cs b/doc/samples/SqlConnection_AccessTokenCallback.cs index a195e48585..c761f640fd 100644 --- a/doc/samples/SqlConnection_AccessTokenCallback.cs +++ b/doc/samples/SqlConnection_AccessTokenCallback.cs @@ -18,12 +18,12 @@ private static void OpenSqlConnection() using (SqlConnection connection = new SqlConnection("Data Source=contoso.database.windows.net;Initial Catalog=AdventureWorks;") { AccessTokenCallback = async (authParams, cancellationToken) => - { - var cred = new DefaultAzureCredential(); - string scope = authParams.Resource.EndsWith(s_defaultScopeSuffix) ? authParams.Resource : authParams.Resource + s_defaultScopeSuffix; - var token = await cred.GetTokenAsync(new TokenRequestContext(new[] { scope }), cancellationToken); - return new SqlAuthenticationToken(token.Token, token.ExpiresOn); - } + { + var cred = new DefaultAzureCredential(); + string scope = authParams.Resource.EndsWith(s_defaultScopeSuffix) ? authParams.Resource : authParams.Resource + s_defaultScopeSuffix; + var token = await cred.GetTokenAsync(new TokenRequestContext(new[] { scope }), cancellationToken); + return new SqlAuthenticationToken(token.Token, token.ExpiresOn); + } }) { connection.Open(); @@ -31,4 +31,5 @@ private static void OpenSqlConnection() Console.WriteLine("State: {0}", connection.State); } } +} // diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index ef6a3e254f..cbf8a08916 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -4,9 +4,9 @@ // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. // New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. -using System.Threading.Tasks; -using System.Threading; using System; +using System.Threading; +using System.Threading.Tasks; [assembly: System.CLSCompliant(true)] namespace Microsoft.Data diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index c86d3442c5..d76d516aad 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -620,16 +620,18 @@ public override string ConnectionString CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); } - else if (_accessToken != null) + + if (_accessToken != null) { CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions); } - else if (_accessTokenCallback != null) + + if (_accessTokenCallback != null) { CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(connectionOptions); } } - ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken)); + ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken, _accessTokenCallback)); _connectionString = value; // Change _connectionString value only after value is validated CacheConnectionStringProperties(); } @@ -689,7 +691,7 @@ public string AccessToken } // Need to call ConnectionString_Set to do proper pool group check - ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value)); + ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value, accessTokenCallback: null)); _accessToken = value; } } @@ -711,7 +713,7 @@ public Func - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string.. /// - internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword { + internal static string ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback { get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. - /// - internal static string ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback { - get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback", resourceCulture); + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback", resourceCulture); } } @@ -483,15 +474,6 @@ internal static string ADP_InvalidMixedUsageOfCredentialAndAccessToken { } } - /// - /// Looks up a localized string similar to Cannot set the Credential property if the AccessTokenCallback property is already set.. - /// - internal static string ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback { - get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cannot use Credential with UserID, UID, Password, or PWD connection string keywords.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 5d81d2fcdf..9e048862b3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1947,13 +1947,7 @@ Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. - - Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. - - - Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. - - - Cannot set the Credential property if the AccessTokenCallback property is already set. + + Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index a5d2be8022..771615e2cc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -4,9 +4,9 @@ // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. // New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. -using System.Threading.Tasks; -using System.Threading; using System; +using System.Threading; +using System.Threading.Tasks; [assembly: System.CLSCompliant(true)] [assembly: System.Resources.NeutralResourcesLanguageAttribute("en-US")] diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 53acdd0eae..e66f869050 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -738,7 +738,7 @@ public string AccessToken _accessToken = value; // Need to call ConnectionString_Set to do proper pool group check - ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, _accessToken, _serverCertificateValidationCallback, _clientCertificateRetrievalCallback, _originalNetworkAddressInfo)); + ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, _accessToken, _serverCertificateValidationCallback, _clientCertificateRetrievalCallback, _originalNetworkAddressInfo, null)); } } @@ -759,7 +759,7 @@ public Func - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string.. + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string.. /// - internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword { + internal static string ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback { get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. - /// - internal static string ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback { - get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback", resourceCulture); + return ResourceManager.GetString("ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback", resourceCulture); } } @@ -960,15 +951,6 @@ internal static string ADP_InvalidMixedUsageOfCredentialAndAccessToken { } } - /// - /// Looks up a localized string similar to Cannot set the Credential property if the AccessTokenCallback property is already set.. - /// - internal static string ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback { - get { - return ResourceManager.GetString("ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cannot use Credential with UserID, UID, Password, or PWD connection string keywords.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index e1ea45c854..12844ad2ba 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4641,13 +4641,7 @@ Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. - - Cannot set the AccessTokenCallback property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string. - - - Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. - - - Cannot set the Credential property if the AccessTokenCallback property is already set. + + Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index d75877f3a2..08e46ed284 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -1273,19 +1273,10 @@ static internal Exception InvalidMixedUsageOfAccessTokenAndTokenCallback() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback)); internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndAuthentication() - => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndTokenCallback)); + => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback)); internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSecurity)); - - internal static Exception InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword() - => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenCallbackAndUserIDPassword)); - - internal static Exception InvalidMixedUsageOfCredentialAndAccessTokenCallback() - => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessTokenCallback)); - - internal static Exception InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback() - => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfActiveDirectoryDefaultTokenAndTokenCallback)); #endregion internal static bool IsEmpty(string str) => string.IsNullOrEmpty(str); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs index 21143cf630..1eed6a229d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs @@ -56,7 +56,7 @@ internal SqlConnectionPoolKey(string connectionString, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, Func> accessTokenCallback = null) : base(connectionString) { - Debug.Assert(_credential == null || _accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); + Debug.Assert(_credential == null || _accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have a value at the same time."); _credential = credential; _accessToken = accessToken; _accessTokenCallback = accessTokenCallback; @@ -68,9 +68,9 @@ internal SqlConnectionPoolKey(string connectionString, #endregion #else #region NET Core - internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback = null) : base(connectionString) + internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken, Func> accessTokenCallback) : base(connectionString) { - Debug.Assert(credential == null || accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have the value at the same time."); + Debug.Assert(credential == null || accessToken == null || accessTokenCallback == null, "Credential, AccessToken, and Callback can't have a value at the same time."); _credential = credential; _accessToken = accessToken; _accessTokenCallback = accessTokenCallback; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs index a39ca72584..8a8cb3772d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -255,7 +255,6 @@ public enum FeatureExtension : uint public const byte FEDAUTHLIB_LIVEID = 0X00; public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01; public const byte FEDAUTHLIB_MSAL = 0x02; - public const byte FEDAUTHLIB_SECURITYTOKEN_CALLBACK = 0x03; public const byte FEDAUTHLIB_RESERVED = 0X7F; public enum FedAuthLibrary : byte @@ -263,8 +262,7 @@ public enum FedAuthLibrary : byte LiveId = FEDAUTHLIB_LIVEID, SecurityToken = FEDAUTHLIB_SECURITYTOKEN, MSAL = FEDAUTHLIB_MSAL, - Default = FEDAUTHLIB_RESERVED, - SecurityTokenCallback = FEDAUTHLIB_SECURITYTOKEN_CALLBACK + Default = FEDAUTHLIB_RESERVED } public const byte MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 039bf2f766..e65fa83370 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -7,6 +7,8 @@ using System.Data.Common; using System.Reflection; using System.Security; +using System.Threading; +using System.Threading.Tasks; using Microsoft.SqlServer.TDS.Servers; using Xunit; @@ -187,5 +189,84 @@ public void ConnectionTestValidCredentialCombination() Assert.Equal(sqlCredential, conn.Credential); } + + [Fact] + public void ConnectionTestAccessTokenCallbackCombinations() + { + var cleartextCredsConnStr = "User=test;Password=test;"; + var sspiConnStr = "Integrated Security=true;"; + var authConnStr = "Authentication=ActiveDirectoryPassword"; + var testPassword = new SecureString(); + testPassword.MakeReadOnly(); + var sqlCredential = new SqlCredential(string.Empty, testPassword); + Func> callback = (ctx, token) => + Task.FromResult(new SqlAuthenticationToken("invalid", DateTimeOffset.MaxValue)); + + // Successes + using (var conn = new SqlConnection(cleartextCredsConnStr)) + { + conn.AccessTokenCallback = callback; + } + + using (var conn = new SqlConnection(string.Empty, sqlCredential)) + { + conn.AccessTokenCallback = callback; + } + + using (var conn = new SqlConnection() + { + AccessTokenCallback = callback + }) + { + conn.Credential = sqlCredential; + } + + using (var conn = new SqlConnection() + { + AccessTokenCallback = callback + }) + { + conn.ConnectionString = cleartextCredsConnStr; + } + + //Failures + using (var conn = new SqlConnection(sspiConnStr)) + { + Assert.Throws(() => + { + conn.AccessTokenCallback = callback; + }); + } + + using (var conn = new SqlConnection(authConnStr)) + { + Assert.Throws(() => + { + conn.AccessTokenCallback = callback; + }); + } + + using (var conn = new SqlConnection() + { + AccessTokenCallback = callback + }) + { + Assert.Throws(() => + { + conn.ConnectionString = sspiConnStr; + }); + } + + using (var conn = new SqlConnection() + { + AccessTokenCallback = callback + }) + { + Assert.Throws(() => + { + conn.ConnectionString = authConnStr; + }); + } + } } } From 2ab55b8cdef348bb2f3ebe5bb67640474f4db3d5 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 6 Jun 2023 16:17:31 -0500 Subject: [PATCH 27/33] fb --- doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml | 2 +- .../netcore/ref/Microsoft.Data.SqlClient.cs | 5 +---- .../netfx/ref/Microsoft.Data.SqlClient.cs | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 001e47c38f..2ac639b831 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -132,7 +132,7 @@ using (SqlConnection connection = new SqlConnection(connectionString)) Gets or sets the access token callback for the connection. - The Func that takes a and and returns a . + The Func that takes a and and returns a . - public Func> AccessTokenCallback { get { throw null; } set { } } + public System.Func> AccessTokenCallback { get { throw null; } set { } } /// /// for internal test only diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 783f513913..4663cc151f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -4,9 +4,6 @@ // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. // New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. -using System; -using System.Threading; -using System.Threading.Tasks; [assembly: System.CLSCompliant(true)] [assembly: System.Resources.NeutralResourcesLanguageAttribute("en-US")] @@ -772,7 +769,7 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public string AccessToken { get { throw null; } set { } } /// - public Func> AccessTokenCallback { get { throw null; } set { } } + public System.Func> AccessTokenCallback { get { throw null; } set { } } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public System.Guid ClientConnectionId { get { throw null; } } From ada78c2a8b367bbe4e6867712b37f17fff1911fa Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 6 Jun 2023 16:17:51 -0500 Subject: [PATCH 28/33] Apply suggestions from code review Co-authored-by: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> --- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 5 +---- .../netcore/src/Resources/Strings.resx | 2 +- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 5 +---- .../netfx/src/Resources/Strings.resx | 2 +- .../ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs | 6 +++--- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 1527dd6933..cc727103df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -481,10 +481,7 @@ internal SqlInternalConnectionTds( _accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken); } - if (accessTokenCallback != null) - { - _accessTokenCallback = accessTokenCallback; - } + _accessTokenCallback = accessTokenCallback; _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index a8448cf718..7f52b2556f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1948,6 +1948,6 @@ Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. - Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string. + Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 641d428c08..f6b81a533c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -490,10 +490,7 @@ internal SqlInternalConnectionTds( _accessTokenInBytes = System.Text.Encoding.Unicode.GetBytes(accessToken); } - if (accessTokenCallback != null) - { - _accessTokenCallback = accessTokenCallback; - } + _accessTokenCallback = accessTokenCallback; _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 12844ad2ba..2b2374e490 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4642,6 +4642,6 @@ Cannot set the AccessTokenCallback property if the 'Integrated Security' connection string keyword has been set to 'true' or 'SSPI'. - Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string. + Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string. \ No newline at end of file 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 e745309ccf..5eb448e4d2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -568,7 +568,7 @@ public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() Task.FromResult(new SqlAuthenticationToken("my token", DateTimeOffset.MaxValue)); conn.Open(); - Assert.True(conn.State == System.Data.ConnectionState.Open); + Assert.NotEqual(System.Data.ConnectionState.Open, conn.State); } }); @@ -588,7 +588,7 @@ public static void AccessTokenCallbackMustPass() conn.AccessTokenCallback = (ctx, cancellationToken) => { string scope = ctx.Resource.EndsWith(defaultScopeSuffix) ? ctx.Resource : ctx.Resource + defaultScopeSuffix; - var token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); + AccessToken token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); }; conn.Open(); @@ -612,7 +612,7 @@ public static void AccessTokenCallbackReceivesUsernameAndPassword() Assert.Equal(userId, parms.UserId); Assert.Equal(pwd, parms.Password); string scope = parms.Resource.EndsWith(defaultScopeSuffix) ? parms.Resource : parms.Resource + defaultScopeSuffix; - var token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); + AccessToken token = cred.GetToken(new TokenRequestContext(new[] { scope }), cancellationToken); return Task.FromResult(new SqlAuthenticationToken(token.Token, token.ExpiresOn)); }; conn.Open(); From caa6c3e35c68dfbc8412aaddd662cfd247d5cc6f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 6 Jun 2023 16:18:25 -0500 Subject: [PATCH 29/33] fb --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index a4b7df5045..daeaf68c7a 100644 --- a/.gitignore +++ b/.gitignore @@ -361,5 +361,3 @@ MigrationBackup/ # Config Json file **/config.json - -.idea/ From 36a59e8916bee0de84dae73756dcab17215a7ba0 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 6 Jun 2023 16:38:49 -0500 Subject: [PATCH 30/33] Apply suggestions from code review Co-authored-by: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> --- .../netcore/src/Resources/Strings.Designer.cs | 2 +- .../netfx/src/Resources/Strings.Designer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index 8491468f48..cf900c4553 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -457,7 +457,7 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSe } /// - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string.. + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. /// internal static string ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback { get { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 580896e6c2..5fd4a54382 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -934,7 +934,7 @@ internal static string ADP_InvalidMixedUsageOfAccessTokenCallbackAndIntegratedSe } /// - /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication' has been specified in the connection string.. + /// Looks up a localized string similar to Cannot set the AccessTokenCallback property if 'Authentication=Active Directory Default' has been specified in the connection string.. /// internal static string ADP_InvalidMixedUsageOfAuthenticationAndTokenCallback { get { From 16693f686a160581540911acd867b216a5b4162c Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 6 Jun 2023 16:39:33 -0500 Subject: [PATCH 31/33] fb --- .../netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 0b67ce1eeb..905fedb8e8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -717,7 +717,7 @@ public Func Date: Tue, 13 Jun 2023 16:46:48 -0500 Subject: [PATCH 32/33] fb --- doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml | 1 + .../netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ---- .../netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 2ac639b831..08a9cc88fd 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -143,6 +143,7 @@ using (SqlConnection connection = new SqlConnection(connectionString)) ]]> + The AccessTokenCallback is combined with other conflicting authentication configurations. To be added. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 905fedb8e8..cf13db3389 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -715,10 +715,6 @@ public Func Date: Fri, 16 Jun 2023 17:37:54 -0700 Subject: [PATCH 33/33] Add tests --- .../tests/FunctionalTests/SqlConnectionBasicTests.cs | 2 ++ .../SQL/ConnectivityTests/AADConnectionTest.cs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index ce19ee663a..03e6e7b5c7 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -282,10 +282,12 @@ public void ConnectionTestAccessTokenCallbackCombinations() using (var conn = new SqlConnection(cleartextCredsConnStr)) { conn.AccessTokenCallback = callback; + conn.AccessTokenCallback = null; } using (var conn = new SqlConnection(string.Empty, sqlCredential)) { + conn.AccessTokenCallback = null; conn.AccessTokenCallback = callback; } 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 5eb448e4d2..52e5705140 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -575,9 +575,9 @@ public static void ActiveDirectoryDefaultWithAccessTokenCallbackMustFail() 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))] - public static void AccessTokenCallbackMustPass() + public static void AccessTokenCallbackMustOpenPassAndChangePropertyFail() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys); @@ -592,6 +592,11 @@ public static void AccessTokenCallbackMustPass() 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); } }