Skip to content

Commit 39b13a6

Browse files
dauinsightDavid-EngelJavad Rahnamaarellegue
authored
[3.1.6] Test | Updating tests to acquire token from user-assigned managed identity (#2360) (#2473) (#2512)
* Test | Updating tests to acquire token from user-assigned managed identity (#2360) (#2473) --------- Co-authored-by: David Engel <v-davidengel@microsoft.com> Co-authored-by: Javad Rahnama <v-jarahn@microsoft.com> Co-authored-by: David Engel <dengel1012@gmail.com> Co-authored-by: Aris Rellegue <134557572+arellegue@users.noreply.github.com>
1 parent a73c7ee commit 39b13a6

File tree

10 files changed

+32
-87
lines changed

10 files changed

+32
-87
lines changed

BUILDGUIDE.md

-2
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,6 @@ Manual Tests require the below setup to run:
118118
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
119119
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
120120
|AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ |
121-
|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}_ |
122-
|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}_ |
123121
|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.|
124122
|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`|
125123
|SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`|

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

+4-9
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

@@ -35,8 +33,7 @@ public static void LegacyAuthenticationCallbackTest()
3533
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
3634
public static void TokenCredentialTest()
3735
{
38-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
39-
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
36+
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
4037
byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey);
4138
byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek);
4239

@@ -49,8 +46,7 @@ public static void TokenCredentialRotationTest()
4946
// SqlClientCustomTokenCredential implements a legacy authentication callback to request the access token from the client-side.
5047
SqlColumnEncryptionAzureKeyVaultProvider oldAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential());
5148

52-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
53-
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
49+
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
5450

5551
byte[] encryptedCekWithNewProvider = newAkvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey);
5652
byte[] decryptedCekWithOldProvider = oldAkvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCekWithNewProvider);
@@ -72,15 +68,14 @@ public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion()
7268
{
7369
string keyName = keyPathUri.Segments[2];
7470
string keyVersion = keyPathUri.Segments[3];
75-
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
76-
KeyClient keyClient = new KeyClient(vaultUri, clientSecretCredential);
71+
KeyClient keyClient = new KeyClient(vaultUri, DataTestUtility.GetTokenCredential());
7772
KeyVaultKey currentVersionKey = keyClient.GetKey(keyName);
7873
KeyVaultKey specifiedVersionKey = keyClient.GetKey(keyName, keyVersion);
7974

8075
//If specified versioned key is the most recent version of the key then we cannot test.
8176
if (!KeyIsLatestVersion(specifiedVersionKey, currentVersionKey))
8277
{
83-
SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
78+
SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
8479
// Perform an operation to initialize the internal caches
8580
azureKeyProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVOriginalUrl, EncryptionAlgorithm, s_columnEncryptionKey);
8681

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

+1-2
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

-14
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

+10-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
using Microsoft.Identity.Client;
1919
using Microsoft.Data.SqlClient.TestUtilities;
2020
using Xunit;
21+
using Azure.Identity;
22+
using Azure.Core;
2123

2224
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
2325
{
@@ -37,8 +39,6 @@ public static class DataTestUtility
3739
public static readonly string AKVUrl = null;
3840
public static readonly string AKVOriginalUrl = null;
3941
public static readonly string AKVTenantId = null;
40-
public static readonly string AKVClientId = null;
41-
public static readonly string AKVClientSecret = null;
4242
public static readonly string LocalDbAppName = null;
4343
public static List<string> AEConnStrings = new List<string>();
4444
public static List<string> AEConnStringsSetup = new List<string>();
@@ -122,8 +122,6 @@ static DataTestUtility()
122122
}
123123

124124
AKVTenantId = c.AzureKeyVaultTenantId;
125-
AKVClientId = c.AzureKeyVaultClientId;
126-
AKVClientSecret = c.AzureKeyVaultClientSecret;
127125

128126
if (EnclaveEnabled)
129127
{
@@ -316,7 +314,14 @@ public static bool IsNotAzureServer()
316314
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse
317315
public static bool IsAKVSetupAvailable()
318316
{
319-
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
317+
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
318+
}
319+
320+
private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId });
321+
322+
public static TokenCredential GetTokenCredential()
323+
{
324+
return s_defaultCredential;
320325
}
321326

322327
public static bool IsUsingManagedSNI() => UseManagedSNIOnWindows;

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

+17-47
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

-2
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,11 @@
304304
<ItemGroup>
305305
<ProjectReference Include="$(AddOnsPath)AzureKeyVaultProvider\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj" />
306306
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
307-
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryVersion)" />
308307
<PackageReference Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerVersion)" />
309308
<PackageReference Include="System.Runtime.Caching" Version="$(SystemRuntimeCachingVersion)" />
310309
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
311310
<PackageReference Include="System.Linq.Expressions" Version="$(SystemLinqExpressionsVersion)" />
312311
<PackageReference Include="System.Net.Sockets" Version="$(SystemNetSocketsVersion)" />
313-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="$(SystemIdentityModelTokensJwtVersion)" />
314312
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp' AND $(OS)=='Unix'" Include="Microsoft.Windows.Compatibility" Version="$(MicrosoftWindowsCompatibilityVersion)" />
315313
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotnetRemoteExecutorVersion)" />
316314
</ItemGroup>

src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs

-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ public class Config
2222
public string AADServicePrincipalSecret = null;
2323
public string AzureKeyVaultURL = null;
2424
public string AzureKeyVaultTenantId = null;
25-
public string AzureKeyVaultClientId = null;
26-
public string AzureKeyVaultClientSecret = null;
2725
public string LocalDbAppName = null;
2826
public bool EnclaveEnabled = false;
2927
public bool TracingEnabled = false;

src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json

-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
"AADServicePrincipalSecret": "",
1414
"AzureKeyVaultURL": "",
1515
"AzureKeyVaultTenantId": "",
16-
"AzureKeyVaultClientId": "",
17-
"AzureKeyVaultClientSecret": "",
1816
"SupportsIntegratedSecurity": true,
1917
"LocalDbAppName": "",
2018
"SupportsFileStream": false,

tools/props/Versions.props

-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
<!-- Test Project Dependencies -->
6161
<PropertyGroup>
6262
<MicrosoftDotNetPlatformAbstractionsVersion>3.1.1</MicrosoftDotNetPlatformAbstractionsVersion>
63-
<MicrosoftIdentityModelClientsActiveDirectoryVersion>5.2.6</MicrosoftIdentityModelClientsActiveDirectoryVersion>
6463
<MicrosoftNETTestSdkVersion>15.9.0</MicrosoftNETTestSdkVersion>
6564
<MicrosoftWindowsCompatibilityVersion>3.1.0</MicrosoftWindowsCompatibilityVersion>
6665
<NewtonsoftJsonVersion>12.0.3</NewtonsoftJsonVersion>
@@ -69,7 +68,6 @@
6968
<SystemDataOdbcVersion21>4.5.0</SystemDataOdbcVersion21>
7069
<SystemDataOdbcVersion>4.6.0</SystemDataOdbcVersion>
7170
<SystemNetSocketsVersion>4.3.0</SystemNetSocketsVersion>
72-
<SystemIdentityModelTokensJwtVersion>6.8.0</SystemIdentityModelTokensJwtVersion>
7371
<XunitVersion>2.4.1</XunitVersion>
7472
<MicrosoftDotNetRemoteExecutorVersion>5.0.0-beta.20206.4</MicrosoftDotNetRemoteExecutorVersion>
7573
<MicrosoftNETCoreRuntimeCoreCLRVersion>2.0.8</MicrosoftNETCoreRuntimeCoreCLRVersion>

0 commit comments

Comments
 (0)