Skip to content

Commit 070d343

Browse files
authored
[4.0.6] Test | Updating tests to acquire token from user-assigned managed identity (#2360) (#2473) (#2500)
1 parent af91ad0 commit 070d343

File tree

10 files changed

+32
-87
lines changed

10 files changed

+32
-87
lines changed

BUILDGUIDE.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,6 @@ Manual Tests require the below setup to run:
169169
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
170170
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
171171
|AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ |
172-
|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}_ |
173-
|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}_ |
174172
|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.|
175173
|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. |
176174
|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`|

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 & 5 deletions
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
{
@@ -36,8 +38,6 @@ public static class DataTestUtility
3638
public static readonly string AKVUrl = null;
3739
public static readonly string AKVOriginalUrl = null;
3840
public static readonly string AKVTenantId = null;
39-
public static readonly string AKVClientId = null;
40-
public static readonly string AKVClientSecret = null;
4141
public static readonly string LocalDbAppName = null;
4242
public static readonly string LocalDbSharedInstanceName = null;
4343
public static List<string> AEConnStrings = new List<string>();
@@ -132,8 +132,6 @@ static DataTestUtility()
132132
}
133133

134134
AKVTenantId = c.AzureKeyVaultTenantId;
135-
AKVClientId = c.AzureKeyVaultClientId;
136-
AKVClientSecret = c.AzureKeyVaultClientSecret;
137135

138136
if (EnclaveEnabled)
139137
{
@@ -320,7 +318,14 @@ public static bool IsNotAzureServer()
320318
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse
321319
public static bool IsAKVSetupAvailable()
322320
{
323-
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
321+
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
322+
}
323+
324+
private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId });
325+
326+
public static TokenCredential GetTokenCredential()
327+
{
328+
return s_defaultCredential;
324329
}
325330

326331
public static bool IsUsingManagedSNI() => UseManagedSNIOnWindows;

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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,11 @@
309309
<ItemGroup>
310310
<ProjectReference Include="$(AddOnsPath)AzureKeyVaultProvider\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj" />
311311
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
312-
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryVersion)" />
313312
<PackageReference Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerVersion)" />
314313
<PackageReference Include="System.Runtime.Caching" Version="$(SystemRuntimeCachingVersion)" />
315314
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
316315
<PackageReference Include="System.Linq.Expressions" Version="$(SystemLinqExpressionsVersion)" />
317316
<PackageReference Include="System.Net.Sockets" Version="$(SystemNetSocketsVersion)" />
318-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="$(SystemIdentityModelTokensJwtVersion)" />
319317
<PackageReference Condition="'$(TargetGroup)'=='netfx'" Include="Microsoft.SqlServer.Types" Version="$(MicrosoftSqlServerTypesVersion)" />
320318
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp' AND $(OS)=='Unix'" Include="Microsoft.Windows.Compatibility" Version="$(MicrosoftWindowsCompatibilityVersion)" />
321319
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotnetRemoteExecutorVersion)" />

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ public class Config
2121
public string AADServicePrincipalSecret = null;
2222
public string AzureKeyVaultURL = null;
2323
public string AzureKeyVaultTenantId = null;
24-
public string AzureKeyVaultClientId = null;
25-
public string AzureKeyVaultClientSecret = null;
2624
public string LocalDbAppName = null;
2725
public string LocalDbSharedInstanceName = null;
2826
public bool EnclaveEnabled = false;

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
"AADServicePrincipalSecret": "",
1313
"AzureKeyVaultURL": "",
1414
"AzureKeyVaultTenantId": "",
15-
"AzureKeyVaultClientId": "",
16-
"AzureKeyVaultClientSecret": "",
1715
"SupportsIntegratedSecurity": true,
1816
"LocalDbAppName": "",
1917
"LocalDbSharedInstanceName":"",

tools/props/Versions.props

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
<!-- Test Project Dependencies -->
5757
<PropertyGroup>
5858
<MicrosoftDotNetPlatformAbstractionsVersion>3.1.1</MicrosoftDotNetPlatformAbstractionsVersion>
59-
<MicrosoftIdentityModelClientsActiveDirectoryVersion>5.2.6</MicrosoftIdentityModelClientsActiveDirectoryVersion>
6059
<MicrosoftNETTestSdkVersion>15.9.0</MicrosoftNETTestSdkVersion>
6160
<MicrosoftWindowsCompatibilityVersion>3.1.0</MicrosoftWindowsCompatibilityVersion>
6261
<NewtonsoftJsonVersion>13.0.1</NewtonsoftJsonVersion>
@@ -65,7 +64,6 @@
6564
<SystemDataOdbcVersion21>4.5.0</SystemDataOdbcVersion21>
6665
<SystemDataOdbcVersion>4.6.0</SystemDataOdbcVersion>
6766
<SystemNetSocketsVersion>4.3.0</SystemNetSocketsVersion>
68-
<SystemIdentityModelTokensJwtVersion>6.8.0</SystemIdentityModelTokensJwtVersion>
6967
<XunitVersion>2.4.1</XunitVersion>
7068
<MicrosoftDotNetRemoteExecutorVersion>5.0.0-beta.20206.4</MicrosoftDotNetRemoteExecutorVersion>
7169
<MicrosoftNETCoreRuntimeCoreCLRVersion>2.0.8</MicrosoftNETCoreRuntimeCoreCLRVersion>

0 commit comments

Comments
 (0)