Skip to content

Commit e5ba166

Browse files
author
SqlClient Azure DevOps
committed
Merge in 'release/5.2' changes
2 parents 2e23a55 + 51d58fa commit e5ba166

File tree

13 files changed

+36
-109
lines changed

13 files changed

+36
-109
lines changed

BUILDGUIDE.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ Manual Tests require the below setup to run:
165165
|TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;` <br/> OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`|
166166
|NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;` <br/> OR <br/> `Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`|
167167
|TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`|
168-
|TCPConnectionStringAASVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`|
169168
|TCPConnectionStringNoneVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using None Attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = NONE;`|
170169
|TCPConnectionStringAASSGX | (Optional) Connection String for a TCP enabled SQL Server with a SGX Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`|
171170
|EnclaveEnabled | Enables tests requiring an enclave-configured server.|
@@ -176,8 +175,6 @@ Manual Tests require the below setup to run:
176175
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
177176
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
178177
|AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ |
179-
|AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ |
180-
|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ |
181178
|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`|
182179
|LocalDbAppName | (Optional) If Local Db Testing is supported, this property configures the name of Local DB App instance available in client environment. Empty string value disables Local Db testing. | Name of Local Db App to connect to.|
183180
|LocalDbSharedInstanceName | (Optional) If LocalDB testing is supported and the instance is shared, this property configures the name of the shared instance of LocalDB to connect to. | Name of shared instance of LocalDB. |

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,10 +653,10 @@ private bool InferConnectionDetails()
653653

654654
Port = port;
655655
}
656-
// Instance Name Handling.
657-
if (backSlashIndex > -1)
656+
// Instance Name Handling. Only if we found a '\' and we did not find a port in the Data Source
657+
else if (backSlashIndex > -1)
658658
{
659-
// This means that there is a part separated by '\'
659+
// This means that there will not be any part separated by comma.
660660
InstanceName = tokensByCommaAndSlash[1].Trim();
661661

662662
if (string.IsNullOrWhiteSpace(InstanceName))

src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
// See the LICENSE file in the project root for more information.
44

55
using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
6-
using Azure.Identity;
76
using Xunit;
87
using Azure.Security.KeyVault.Keys;
98
using System.Reflection;
109
using System;
11-
using System.Linq;
1210
using System.Collections.Generic;
1311
using System.Threading;
1412
using System.Diagnostics.Tracing;
@@ -86,8 +84,7 @@ public static void TokenCredentialTest()
8684
Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid();
8785
using DataTestUtility.AKVEventListener AKVListener = new();
8886

89-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
90-
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
87+
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
9188
byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey);
9289
byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek);
9390

@@ -104,8 +101,7 @@ public static void TokenCredentialRotationTest()
104101
// SqlClientCustomTokenCredential implements a legacy authentication callback to request the access token from the client-side.
105102
SqlColumnEncryptionAzureKeyVaultProvider oldAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential());
106103

107-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
108-
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
104+
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
109105

110106
byte[] encryptedCekWithNewProvider = newAkvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey);
111107
byte[] decryptedCekWithOldProvider = oldAkvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCekWithNewProvider);
@@ -129,15 +125,14 @@ public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion()
129125
{
130126
string keyName = keyPathUri.Segments[2];
131127
string keyVersion = keyPathUri.Segments[3];
132-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
133-
KeyClient keyClient = new KeyClient(vaultUri, clientSecretCredential);
128+
KeyClient keyClient = new KeyClient(vaultUri, DataTestUtility.GetTokenCredential());
134129
KeyVaultKey currentVersionKey = keyClient.GetKey(keyName);
135130
KeyVaultKey specifiedVersionKey = keyClient.GetKey(keyName, keyVersion);
136131

137132
//If specified versioned key is the most recent version of the key then we cannot test.
138133
if (!KeyIsLatestVersion(specifiedVersionKey, currentVersionKey))
139134
{
140-
SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
135+
SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
141136
// Perform an operation to initialize the internal caches
142137
azureKeyProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVOriginalUrl, EncryptionAlgorithm, s_columnEncryptionKey);
143138

src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ internal static X509Certificate2 CreateCertificate()
141141

142142
private static async Task SetupAKVKeysAsync()
143143
{
144-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
145-
KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, clientSecretCredential);
144+
KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential());
146145
AsyncPageable<KeyProperties> keys = keyClient.GetPropertiesOfKeysAsync();
147146
IAsyncEnumerator<KeyProperties> enumerator = keys.GetAsyncEnumerator();
148147

src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,11 @@
77
using System.Text.RegularExpressions;
88
using System.Threading;
99
using System.Threading.Tasks;
10-
using Microsoft.IdentityModel.Clients.ActiveDirectory;
1110

1211
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
1312
{
1413
public static class AADUtility
1514
{
16-
public static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope)
17-
{
18-
var authContext = new AuthenticationContext(authority);
19-
ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
20-
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
21-
if (result == null)
22-
{
23-
throw new Exception($"Failed to retrieve an access token for {resource}");
24-
}
25-
26-
return result.AccessToken;
27-
}
28-
2915
public static async Task<string> GetManagedIdentityToken(string clientId = null) =>
3016
await new MockManagedIdentityTokenProvider().AcquireTokenAsync(clientId).ConfigureAwait(false);
3117

src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
using System.Text;
2323
using System.Security.Principal;
2424
using System.Runtime.InteropServices;
25+
using Azure.Identity;
26+
using Azure.Core;
2527

2628
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
2729
{
@@ -30,7 +32,6 @@ public static class DataTestUtility
3032
public static readonly string NPConnectionString = null;
3133
public static readonly string TCPConnectionString = null;
3234
public static readonly string TCPConnectionStringHGSVBS = null;
33-
public static readonly string TCPConnectionStringAASVBS = null;
3435
public static readonly string TCPConnectionStringNoneVBS = null;
3536
public static readonly string TCPConnectionStringAASSGX = null;
3637
public static readonly string AADAuthorityURL = null;
@@ -41,8 +42,6 @@ public static class DataTestUtility
4142
public static readonly string AKVUrl = null;
4243
public static readonly string AKVOriginalUrl = null;
4344
public static readonly string AKVTenantId = null;
44-
public static readonly string AKVClientId = null;
45-
public static readonly string AKVClientSecret = null;
4645
public static readonly string LocalDbAppName = null;
4746
public static readonly string LocalDbSharedInstanceName = null;
4847
public static List<string> AEConnStrings = new List<string>();
@@ -144,7 +143,6 @@ static DataTestUtility()
144143
NPConnectionString = c.NPConnectionString;
145144
TCPConnectionString = c.TCPConnectionString;
146145
TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS;
147-
TCPConnectionStringAASVBS = c.TCPConnectionStringAASVBS;
148146
TCPConnectionStringNoneVBS = c.TCPConnectionStringNoneVBS;
149147
TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX;
150148
AADAuthorityURL = c.AADAuthorityURL;
@@ -194,8 +192,6 @@ static DataTestUtility()
194192
}
195193

196194
AKVTenantId = c.AzureKeyVaultTenantId;
197-
AKVClientId = c.AzureKeyVaultClientId;
198-
AKVClientSecret = c.AzureKeyVaultClientSecret;
199195

200196
if (EnclaveEnabled)
201197
{
@@ -205,11 +201,6 @@ static DataTestUtility()
205201
AEConnStringsSetup.Add(TCPConnectionStringHGSVBS);
206202
}
207203

208-
if (!string.IsNullOrEmpty(TCPConnectionStringAASVBS))
209-
{
210-
AEConnStrings.Add(TCPConnectionStringAASVBS);
211-
}
212-
213204
if (!string.IsNullOrEmpty(TCPConnectionStringNoneVBS))
214205
{
215206
AEConnStrings.Add(TCPConnectionStringNoneVBS);
@@ -458,7 +449,14 @@ public static bool IsNotAzureServer()
458449
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse
459450
public static bool IsAKVSetupAvailable()
460451
{
461-
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
452+
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
453+
}
454+
455+
private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId });
456+
457+
public static TokenCredential GetTokenCredential()
458+
{
459+
return s_defaultCredential;
462460
}
463461

464462
public static bool IsTargetReadyForAeWithKeyStore()

src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6-
using System.IdentityModel.Tokens.Jwt;
6+
using System.Collections.Concurrent;
77
using System.Linq;
88
using System.Net.Http;
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Azure.Core;
12-
using Microsoft.IdentityModel.Clients.ActiveDirectory;
13-
using Newtonsoft.Json;
14-
using Newtonsoft.Json.Linq;
12+
using Azure.Identity;
1513

1614
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
1715
{
1816
public class SqlClientCustomTokenCredential : TokenCredential
1917
{
18+
private const string DEFAULT_PREFIX = "/.default";
19+
2020
string _authority = "";
2121
string _resource = "";
2222
string _akvUrl = "";
@@ -70,40 +70,8 @@ private async Task<AccessToken> AcquireTokenAsync()
7070
_akvUrl = DataTestUtility.AKVUrl;
7171
}
7272

73-
string strAccessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource);
74-
DateTime expiryTime = InterceptAccessTokenForExpiry(strAccessToken);
75-
return new AccessToken(strAccessToken, new DateTimeOffset(expiryTime));
76-
}
77-
78-
private DateTime InterceptAccessTokenForExpiry(string accessToken)
79-
{
80-
if (null == accessToken)
81-
{
82-
throw new ArgumentNullException(accessToken);
83-
}
84-
85-
var jwtHandler = new JwtSecurityTokenHandler();
86-
var jwtOutput = string.Empty;
87-
88-
// Check Token Format
89-
if (!jwtHandler.CanReadToken(accessToken))
90-
throw new FormatException(accessToken);
91-
92-
JwtSecurityToken token = jwtHandler.ReadJwtToken(accessToken);
93-
94-
// Re-serialize the Token Headers to just Key and Values
95-
var jwtHeader = JsonConvert.SerializeObject(token.Header.Select(h => new { h.Key, h.Value }));
96-
jwtOutput = $"{{\r\n\"Header\":\r\n{JToken.Parse(jwtHeader)},";
97-
98-
// Re-serialize the Token Claims to just Type and Values
99-
var jwtPayload = JsonConvert.SerializeObject(token.Claims.Select(c => new { c.Type, c.Value }));
100-
jwtOutput += $"\r\n\"Payload\":\r\n{JToken.Parse(jwtPayload)}\r\n}}";
101-
102-
// Output the whole thing to pretty JSON object formatted.
103-
string jToken = JToken.Parse(jwtOutput).ToString(Formatting.Indented);
104-
JToken payload = JObject.Parse(jToken).GetValue("Payload");
105-
106-
return new DateTime(1970, 1, 1).AddSeconds((long)payload[4]["Value"]);
73+
AccessToken accessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource);
74+
return accessToken;
10775
}
10876

10977
private static string ValidateChallenge(string challenge)
@@ -127,16 +95,18 @@ private static string ValidateChallenge(string challenge)
12795
/// <param name="authority">Authorization URL</param>
12896
/// <param name="resource">Resource</param>
12997
/// <returns></returns>
130-
public static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource)
98+
public static async Task<AccessToken> AzureActiveDirectoryAuthenticationCallback(string authority, string resource)
13199
{
132-
var authContext = new AuthenticationContext(authority);
133-
ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
134-
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
135-
if (result == null)
136-
{
137-
throw new InvalidOperationException($"Failed to retrieve an access token for {resource}");
138-
}
139-
return result.AccessToken;
100+
using CancellationTokenSource cts = new();
101+
cts.CancelAfter(30000); // Hard coded for tests
102+
string[] scopes = new string[] { resource + DEFAULT_PREFIX };
103+
TokenRequestContext tokenRequestContext = new(scopes);
104+
int separatorIndex = authority.LastIndexOf('/');
105+
string authorityHost = authority.Remove(separatorIndex + 1);
106+
string audience = authority.Substring(separatorIndex + 1);
107+
TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authorityHost) };
108+
AccessToken accessToken = await DataTestUtility.GetTokenCredential().GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false);
109+
return accessToken;
140110
}
141111
}
142112
}

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,17 +332,14 @@
332332
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
333333
</ItemGroup>
334334
<ItemGroup>
335-
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
336335
<PackageReference Condition="$(ReferenceType.Contains('Package'))" Include="Microsoft.Identity.Client" Version="$(MicrosoftIdentityClientVersion)" />
337336
<PackageReference Condition="$(ReferenceType.Contains('Package'))" Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryVersion)" />
338337
</ItemGroup>
339338
<ItemGroup>
340339
<ProjectReference Include="$(AddOnsPath)AzureKeyVaultProvider\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj" />
341340
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
342-
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryVersion)" />
343341
<PackageReference Include="System.Runtime.Caching" Version="$(SystemRuntimeCachingVersion)" />
344342
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
345-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="$(SystemIdentityModelTokensJwtVersion)" />
346343
<PackageReference Condition="'$(TargetGroup)'=='netfx'" Include="Microsoft.SqlServer.Types" Version="$(MicrosoftSqlServerTypesVersion)" />
347344
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp'" Include="Microsoft.SqlServer.Types" Version="$(MicrosoftSqlServerTypesVersionNet)" />
348345
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotnetRemoteExecutorVersion)" />

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public static void ConnectManagedWithInstanceNameTest(bool useMultiSubnetFailove
8787
}
8888

8989
#if NETCOREAPP
90+
[ActiveIssue("27824")] // When specifying instance name and port number, this method call always returns false
9091
[ConditionalFact(nameof(IsSPNPortNumberTestForTCP))]
9192
public static void PortNumberInSPNTestForTCP()
9293
{

0 commit comments

Comments
 (0)