Skip to content

Commit 32abbc7

Browse files
authored
Add new trusted AKV URLs for FR and DE (#3482)
- Added 4 new trusted AKV URLs. - Fixed existing manual tests and added unit tests.
1 parent 42146a3 commit 32abbc7

File tree

5 files changed

+164
-19
lines changed

5 files changed

+164
-19
lines changed

src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,28 @@ internal static class Constants
99
/// <summary>
1010
/// Azure Key Vault Domain Name
1111
/// </summary>
12-
internal static readonly string[] AzureKeyVaultPublicDomainNames = new string[] {
13-
@"vault.azure.net", // default
14-
@"vault.azure.cn", // Azure China
15-
@"vault.usgovcloudapi.net", // US Government
16-
@"vault.microsoftazure.de", // Azure Germany
17-
@"managedhsm.azure.net", // public HSM vault
18-
@"managedhsm.azure.cn", // Azure China HSM vault
19-
@"managedhsm.usgovcloudapi.net", // US Government HSM vault
20-
@"managedhsm.microsoftazure.de" // Azure Germany HSM vault
21-
};
12+
internal static readonly string[] AzureKeyVaultPublicDomainNames =
13+
[
14+
// Azure Key Vaults
15+
"vault.azure.net", // Default
16+
"vault.azure.cn", // China
17+
"vault.usgovcloudapi.net", // US Government
18+
"vault.microsoftazure.de", // Azure Germany
19+
"vault.cloudapi.microsoft.scloud", // USSec
20+
"vault.cloudapi.eaglex.ic.gov", // USNat
21+
"vault.sovcloud-api.fr", // France (Bleu)
22+
"vault.sovcloud-api.de", // Germany (Delos)
23+
24+
// Managed High Security Modules (HSM) Vaults
25+
"managedhsm.azure.net",
26+
"managedhsm.azure.cn",
27+
"managedhsm.usgovcloudapi.net",
28+
"managedhsm.microsoftazure.de",
29+
"managedhsm.cloudapi.microsoft.scloud",
30+
"managedhsm.cloudapi.eaglex.ic.gov",
31+
"managedhsm.sovcloud-api.fr",
32+
"managedhsm.sovcloud-api.de"
33+
];
2234

2335
/// <summary>
2436
/// Always Encrypted Parameter names for exec handling

src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Utils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ internal static ArgumentException InvalidAKVPath(string masterKeyPath, bool isSy
141141
internal static ArgumentException InvalidAKVUrl(string masterKeyPath) =>
142142
new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvUrlTemplate, masterKeyPath), Constants.AeParamMasterKeyPath);
143143

144-
internal static Exception InvalidAKVUrlTrustedEndpoints(string masterKeyPath, string endpoints) =>
144+
internal static ArgumentException InvalidAKVUrlTrustedEndpoints(string masterKeyPath, string endpoints) =>
145145
new ArgumentException(string.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvKeyPathTrustedTemplate, masterKeyPath, endpoints),
146146
Constants.AeParamMasterKeyPath);
147147
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,22 @@ public void InvalidCertificatePath()
182182
string dummyPathWithOnlyHost = @"https://www.microsoft.com";
183183
string invalidUrlErrorMessage = $@"Invalid url specified: '{dummyPathWithOnlyHost}'";
184184
string dummyPathWithInvalidKey = @"https://www.microsoft.vault.azure.com/keys/dummykey/dummykeyid";
185-
string invalidTrustedEndpointErrorMessage = $@"Invalid Azure Key Vault key path specified: '{dummyPathWithInvalidKey}'. Valid trusted endpoints: vault.azure.net, vault.azure.cn, vault.usgovcloudapi.net, vault.microsoftazure.de, managedhsm.azure.net, managedhsm.azure.cn, managedhsm.usgovcloudapi.net, managedhsm.microsoftazure.de.\s+\(?Parameter (name: )?'?masterKeyPath('\))?";
186185

187186
Exception ex = Assert.Throws<ArgumentException>(
188187
() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, cek));
189188
Assert.Matches(invalidUrlErrorMessage, ex.Message);
190189

191190
ex = Assert.Throws<ArgumentException>(
192191
() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, cek));
193-
Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message);
192+
Assert.Matches(TrustedUrlsTest.MakeInvalidVaultErrorMessage(dummyPathWithInvalidKey), ex.Message);
194193

195194
ex = Assert.Throws<ArgumentException>(
196195
() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, encryptedCek));
197196
Assert.Matches(invalidUrlErrorMessage, ex.Message);
198197

199198
ex = Assert.Throws<ArgumentException>(
200199
() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, encryptedCek));
201-
Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message);
200+
Assert.Matches(TrustedUrlsTest.MakeInvalidVaultErrorMessage(dummyPathWithInvalidKey), ex.Message);
202201
}
203202

204203
[InlineData(true)]
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Reflection;
7+
using Azure.Core;
8+
using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
9+
using Xunit;
10+
11+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted;
12+
13+
public class TrustedUrlsTest
14+
{
15+
private readonly SqlColumnEncryptionAzureKeyVaultProvider _provider;
16+
private readonly MethodInfo _method;
17+
18+
public TrustedUrlsTest()
19+
{
20+
_provider = new(new SqlClientCustomTokenCredential());
21+
22+
var assembly = typeof(SqlColumnEncryptionAzureKeyVaultProvider).Assembly;
23+
var clazz = assembly.GetType("Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.SqlColumnEncryptionAzureKeyVaultProvider");
24+
_method = clazz.GetMethod(
25+
"ValidateNonEmptyAKVPath",
26+
System.Reflection.BindingFlags.NonPublic |
27+
System.Reflection.BindingFlags.Instance);
28+
}
29+
30+
public const string InvalidVaultKeyPathErrorMessage =
31+
@"Invalid Azure Key Vault key path specified: 'https://www.microsoft.com'. " +
32+
"Valid trusted endpoints: " +
33+
"vault.azure.net, " +
34+
"vault.azure.cn, " +
35+
"vault.usgovcloudapi.net, " +
36+
"vault.microsoftazure.de, " +
37+
"vault.cloudapi.microsoft.scloud, " +
38+
"vault.cloudapi.eaglex.ic.gov, " +
39+
"vault.sovcloud-api.fr, " +
40+
"vault.sovcloud-api.de, " +
41+
"managedhsm.azure.net, " +
42+
"managedhsm.azure.cn, " +
43+
"managedhsm.usgovcloudapi.net, " +
44+
"managedhsm.microsoftazure.de, " +
45+
"managedhsm.cloudapi.microsoft.scloud, " +
46+
"managedhsm.cloudapi.eaglex.ic.gov, " +
47+
"managedhsm.sovcloud-api.fr, " +
48+
"managedhsm.sovcloud-api.de." +
49+
@"\s+\(?Parameter (name: )?'?masterKeyPath('\))?";
50+
51+
private static string MakeUrl(string vault)
52+
{
53+
return $"https://{vault}/keys/dummykey/dummykeyid";
54+
}
55+
56+
public static string MakeInvalidVaultErrorMessage(string url)
57+
{
58+
return
59+
$"Invalid Azure Key Vault key path specified: '{url}'. " +
60+
"Valid trusted endpoints: " +
61+
"vault.azure.net, " +
62+
"vault.azure.cn, " +
63+
"vault.usgovcloudapi.net, " +
64+
"vault.microsoftazure.de, " +
65+
"vault.cloudapi.microsoft.scloud, " +
66+
"vault.cloudapi.eaglex.ic.gov, " +
67+
"vault.sovcloud-api.fr, " +
68+
"vault.sovcloud-api.de, " +
69+
"managedhsm.azure.net, " +
70+
"managedhsm.azure.cn, " +
71+
"managedhsm.usgovcloudapi.net, " +
72+
"managedhsm.microsoftazure.de, " +
73+
"managedhsm.cloudapi.microsoft.scloud, " +
74+
"managedhsm.cloudapi.eaglex.ic.gov, " +
75+
"managedhsm.sovcloud-api.fr, " +
76+
"managedhsm.sovcloud-api.de." +
77+
@"\s+\(?Parameter (name: )?'?masterKeyPath('\))?";
78+
}
79+
80+
[Theory]
81+
[InlineData("www.microsoft.com")]
82+
[InlineData("www.microsoft.vault.azure.com")]
83+
[InlineData("vault.azure.net.io")]
84+
public void InvalidVaults(string vault)
85+
{
86+
// Test that invalid key paths throw and contain the expected error
87+
// message.
88+
var url = MakeUrl(vault);
89+
90+
try
91+
{
92+
_method.Invoke(_provider, new object[] { url, false });
93+
}
94+
catch (TargetInvocationException ex)
95+
{
96+
// Unwrap the exception to get the actual ArgumentException thrown
97+
var argEx = ex.InnerException as ArgumentException;
98+
Assert.NotNull(argEx);
99+
var expected = MakeInvalidVaultErrorMessage(url);
100+
Console.WriteLine("Actual: " + argEx.Message);
101+
Console.WriteLine("Expected: " + expected);
102+
Assert.Matches(expected, argEx.Message);
103+
}
104+
}
105+
106+
[Theory]
107+
// Normal vaults.
108+
[InlineData("vault.azure.net")]
109+
[InlineData("vault.azure.cn")]
110+
[InlineData("vault.usgovcloudapi.net")]
111+
[InlineData("vault.microsoftazure.de")]
112+
[InlineData("vault.cloudapi.microsoft.scloud")]
113+
[InlineData("vault.cloudapi.eaglex.ic.gov")]
114+
[InlineData("vault.sovcloud-api.fr")]
115+
[InlineData("vault.sovcloud-api.de")]
116+
// HSM vaults.
117+
[InlineData("managedhsm.azure.net")]
118+
[InlineData("managedhsm.azure.cn")]
119+
[InlineData("managedhsm.usgovcloudapi.net")]
120+
[InlineData("managedhsm.microsoftazure.de")]
121+
[InlineData("managedhsm.cloudapi.microsoft.scloud")]
122+
[InlineData("managedhsm.cloudapi.eaglex.ic.gov")]
123+
[InlineData("managedhsm.sovcloud-api.fr")]
124+
[InlineData("managedhsm.sovcloud-api.de")]
125+
// Vaults with prefixes.
126+
[InlineData("foo.bar.vault.microsoftazure.de")]
127+
[InlineData("baz.bar.foo.managedhsm.sovcloud-api.fr")]
128+
public void ValidVaults(string vault)
129+
{
130+
// Test that valid vault key paths do not throw exceptions
131+
_method.Invoke(_provider, new object[] { MakeUrl(vault), false });
132+
}
133+
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,23 @@
3636
<ItemGroup Condition="'$(TestSet)' == '' OR '$(TestSet)' == 'AE'">
3737
<Compile Include="AlwaysEncrypted\AKVTests.cs" />
3838
<Compile Include="AlwaysEncrypted\AKVUnitTests.cs" />
39-
<Compile Include="AlwaysEncrypted\EnclaveAzureDatabaseTests.cs" />
40-
<Compile Include="AlwaysEncrypted\ExceptionTestAKVStore.cs" />
41-
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\AKVTestTable.cs" />
42-
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\AkvColumnMasterKey.cs" />
43-
<Compile Include="AlwaysEncrypted\TestFixtures\SQLSetupStrategyAzureKeyVault.cs" />
4439
<Compile Include="AlwaysEncrypted\ApiShould.cs" />
4540
<Compile Include="AlwaysEncrypted\BulkCopyAE.cs" />
4641
<Compile Include="AlwaysEncrypted\BulkCopyAEErrorMessage.cs" />
4742
<Compile Include="AlwaysEncrypted\ColumnDecryptErrorTests.cs" />
43+
<Compile Include="AlwaysEncrypted\EnclaveAzureDatabaseTests.cs" />
4844
<Compile Include="AlwaysEncrypted\End2EndSmokeTests.cs" />
45+
<Compile Include="AlwaysEncrypted\ExceptionTestAKVStore.cs" />
4946
<Compile Include="AlwaysEncrypted\SqlBulkCopyTruncation.cs" />
5047
<Compile Include="AlwaysEncrypted\SqlNullValues.cs" />
48+
<Compile Include="AlwaysEncrypted\TrustedUrlsTest.cs" />
5149
<Compile Include="AlwaysEncrypted\TestFixtures\AzureKeyVaultKeyFixture.cs" />
5250
<Compile Include="AlwaysEncrypted\TestFixtures\DatabaseHelper.cs" />
5351
<Compile Include="AlwaysEncrypted\TestFixtures\SQLSetupStrategy.cs" />
52+
<Compile Include="AlwaysEncrypted\TestFixtures\SQLSetupStrategyAzureKeyVault.cs" />
5453
<Compile Include="AlwaysEncrypted\TestFixtures\SQLSetupStrategyCertStoreProvider.cs" />
54+
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\AKVTestTable.cs" />
55+
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\AkvColumnMasterKey.cs" />
5556
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\ApiTestTable.cs" />
5657
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAETestTable.cs" />
5758
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAEErrorMessageTestTable.cs" />

0 commit comments

Comments
 (0)