Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions BUILDGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,6 @@ Manual Tests require the below setup to run:
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
|AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ |
|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}_ |
|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}_ |
|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.|
|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. |
|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
// See the LICENSE file in the project root for more information.

using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
using Azure.Identity;
using Xunit;
using Azure.Security.KeyVault.Keys;
using System.Reflection;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics.Tracing;
Expand Down Expand Up @@ -86,8 +84,7 @@ public static void TokenCredentialTest()
Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid();
using DataTestUtility.AKVEventListener AKVListener = new();

ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());
byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey);
byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek);

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

ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential);
SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential());

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ internal static X509Certificate2 CreateCertificate()

private static async Task SetupAKVKeysAsync()
{
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, clientSecretCredential);
KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential());
AsyncPageable<KeyProperties> keys = keyClient.GetPropertiesOfKeysAsync();
IAsyncEnumerator<KeyProperties> enumerator = keys.GetAsyncEnumerator();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,11 @@
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
public static class AADUtility
{
public static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
{
throw new Exception($"Failed to retrieve an access token for {resource}");
}

return result.AccessToken;
}

public static async Task<string> GetManagedIdentityToken(string clientId = null) =>
await new MockManagedIdentityTokenProvider().AcquireTokenAsync(clientId).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
using Microsoft.Identity.Client;
using Microsoft.Data.SqlClient.TestUtilities;
using Xunit;
using Azure.Identity;
using Azure.Core;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
Expand All @@ -36,8 +38,6 @@ public static class DataTestUtility
public static readonly string AKVUrl = null;
public static readonly string AKVOriginalUrl = null;
public static readonly string AKVTenantId = null;
public static readonly string AKVClientId = null;
public static readonly string AKVClientSecret = null;
public static readonly string LocalDbAppName = null;
public static readonly string LocalDbSharedInstanceName = null;
public static List<string> AEConnStrings = new List<string>();
Expand Down Expand Up @@ -132,8 +132,6 @@ static DataTestUtility()
}

AKVTenantId = c.AzureKeyVaultTenantId;
AKVClientId = c.AzureKeyVaultClientId;
AKVClientSecret = c.AzureKeyVaultClientSecret;

if (EnclaveEnabled)
{
Expand Down Expand Up @@ -320,7 +318,14 @@ public static bool IsNotAzureServer()
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse
public static bool IsAKVSetupAvailable()
{
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse();
}

private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId });

public static TokenCredential GetTokenCredential()
{
return s_defaultCredential;
}

public static bool IsUsingManagedSNI() => UseManagedSNIOnWindows;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
// See the LICENSE file in the project root for more information.

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Azure.Identity;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
public class SqlClientCustomTokenCredential : TokenCredential
{
private const string DEFAULT_PREFIX = "/.default";

string _authority = "";
string _resource = "";
string _akvUrl = "";
Expand Down Expand Up @@ -70,40 +70,8 @@ private async Task<AccessToken> AcquireTokenAsync()
_akvUrl = DataTestUtility.AKVUrl;
}

string strAccessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource);
DateTime expiryTime = InterceptAccessTokenForExpiry(strAccessToken);
return new AccessToken(strAccessToken, new DateTimeOffset(expiryTime));
}

private DateTime InterceptAccessTokenForExpiry(string accessToken)
{
if (null == accessToken)
{
throw new ArgumentNullException(accessToken);
}

var jwtHandler = new JwtSecurityTokenHandler();
var jwtOutput = string.Empty;

// Check Token Format
if (!jwtHandler.CanReadToken(accessToken))
throw new FormatException(accessToken);

JwtSecurityToken token = jwtHandler.ReadJwtToken(accessToken);

// Re-serialize the Token Headers to just Key and Values
var jwtHeader = JsonConvert.SerializeObject(token.Header.Select(h => new { h.Key, h.Value }));
jwtOutput = $"{{\r\n\"Header\":\r\n{JToken.Parse(jwtHeader)},";

// Re-serialize the Token Claims to just Type and Values
var jwtPayload = JsonConvert.SerializeObject(token.Claims.Select(c => new { c.Type, c.Value }));
jwtOutput += $"\r\n\"Payload\":\r\n{JToken.Parse(jwtPayload)}\r\n}}";

// Output the whole thing to pretty JSON object formatted.
string jToken = JToken.Parse(jwtOutput).ToString(Formatting.Indented);
JToken payload = JObject.Parse(jToken).GetValue("Payload");

return new DateTime(1970, 1, 1).AddSeconds((long)payload[4]["Value"]);
AccessToken accessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource);
return accessToken;
}

private static string ValidateChallenge(string challenge)
Expand All @@ -127,16 +95,18 @@ private static string ValidateChallenge(string challenge)
/// <param name="authority">Authorization URL</param>
/// <param name="resource">Resource</param>
/// <returns></returns>
public static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource)
public static async Task<AccessToken> AzureActiveDirectoryAuthenticationCallback(string authority, string resource)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
{
throw new InvalidOperationException($"Failed to retrieve an access token for {resource}");
}
return result.AccessToken;
using CancellationTokenSource cts = new();
cts.CancelAfter(30000); // Hard coded for tests
string[] scopes = new string[] { resource + DEFAULT_PREFIX };
TokenRequestContext tokenRequestContext = new(scopes);
int separatorIndex = authority.LastIndexOf('/');
string authorityHost = authority.Remove(separatorIndex + 1);
string audience = authority.Substring(separatorIndex + 1);
TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authorityHost) };
AccessToken accessToken = await DataTestUtility.GetTokenCredential().GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false);
return accessToken;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,11 @@
<ItemGroup>
<ProjectReference Include="$(AddOnsPath)AzureKeyVaultProvider\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj" />
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryVersion)" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerVersion)" />
<PackageReference Include="System.Runtime.Caching" Version="$(SystemRuntimeCachingVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
<PackageReference Include="System.Linq.Expressions" Version="$(SystemLinqExpressionsVersion)" />
<PackageReference Include="System.Net.Sockets" Version="$(SystemNetSocketsVersion)" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="$(SystemIdentityModelTokensJwtVersion)" />
<PackageReference Condition="'$(TargetGroup)'=='netfx'" Include="Microsoft.SqlServer.Types" Version="$(MicrosoftSqlServerTypesVersion)" />
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp' AND $(OS)=='Unix'" Include="Microsoft.Windows.Compatibility" Version="$(MicrosoftWindowsCompatibilityVersion)" />
<PackageReference Condition="'$(TargetGroup)'=='netcoreapp'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotnetRemoteExecutorVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public class Config
public string AADServicePrincipalSecret = null;
public string AzureKeyVaultURL = null;
public string AzureKeyVaultTenantId = null;
public string AzureKeyVaultClientId = null;
public string AzureKeyVaultClientSecret = null;
public string LocalDbAppName = null;
public string LocalDbSharedInstanceName = null;
public bool EnclaveEnabled = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
"AADServicePrincipalSecret": "",
"AzureKeyVaultURL": "",
"AzureKeyVaultTenantId": "",
"AzureKeyVaultClientId": "",
"AzureKeyVaultClientSecret": "",
"SupportsIntegratedSecurity": true,
"LocalDbAppName": "",
"LocalDbSharedInstanceName":"",
Expand Down
2 changes: 0 additions & 2 deletions tools/props/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
<!-- Test Project Dependencies -->
<PropertyGroup>
<MicrosoftDotNetPlatformAbstractionsVersion>3.1.1</MicrosoftDotNetPlatformAbstractionsVersion>
<MicrosoftIdentityModelClientsActiveDirectoryVersion>5.2.6</MicrosoftIdentityModelClientsActiveDirectoryVersion>
<MicrosoftNETTestSdkVersion>15.9.0</MicrosoftNETTestSdkVersion>
<MicrosoftWindowsCompatibilityVersion>3.1.0</MicrosoftWindowsCompatibilityVersion>
<NewtonsoftJsonVersion>13.0.1</NewtonsoftJsonVersion>
Expand All @@ -65,7 +64,6 @@
<SystemDataOdbcVersion21>4.5.0</SystemDataOdbcVersion21>
<SystemDataOdbcVersion>4.6.0</SystemDataOdbcVersion>
<SystemNetSocketsVersion>4.3.0</SystemNetSocketsVersion>
<SystemIdentityModelTokensJwtVersion>6.8.0</SystemIdentityModelTokensJwtVersion>
<XunitVersion>2.4.1</XunitVersion>
<MicrosoftDotNetRemoteExecutorVersion>5.0.0-beta.20206.4</MicrosoftDotNetRemoteExecutorVersion>
<MicrosoftNETCoreRuntimeCoreCLRVersion>2.0.8</MicrosoftNETCoreRuntimeCoreCLRVersion>
Expand Down