Skip to content

Commit b401cb2

Browse files
thomhurstasos-tomlonghurstheaths
authored
Allow use of ServiceProvider func to create KeyResolver for ProtectKeysWithAzureKeyVault (Azure#33454)
* Allow use of ServiceProvider func to create KeyResolver for ProtectKeysWithAzureKeyVault call (for resolving nested dependencies) * Export API * Update sdk/extensions/Azure.Extensions.AspNetCore.DataProtection.Keys/src/AzureDataProtectionKeyVaultKeyBuilderExtensions.cs Co-authored-by: Heath Stewart <[email protected]> * Update sdk/extensions/Azure.Extensions.AspNetCore.DataProtection.Keys/src/AzureDataProtectionKeyVaultKeyBuilderExtensions.cs Co-authored-by: Heath Stewart <[email protected]> * Update AzureDataProtectionKeyVaultKeyBuilderExtensions.cs * Remove warning disable line Co-authored-by: Tom Longhurst <[email protected]> Co-authored-by: Heath Stewart <[email protected]>
1 parent bdcc6ac commit b401cb2

File tree

4 files changed

+137
-1
lines changed

4 files changed

+137
-1
lines changed

sdk/extensions/Azure.Extensions.AspNetCore.DataProtection.Keys/api/Azure.Extensions.AspNetCore.DataProtection.Keys.netstandard2.0.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ namespace Microsoft.AspNetCore.DataProtection
33
public static partial class AzureDataProtectionKeyVaultKeyBuilderExtensions
44
{
55
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder builder, string keyIdentifier, Azure.Core.Cryptography.IKeyEncryptionKeyResolver keyResolver) { throw null; }
6+
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder builder, string keyIdentifier, System.Func<System.IServiceProvider, Azure.Core.Cryptography.IKeyEncryptionKeyResolver> keyResolverFactory) { throw null; }
7+
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder builder, string keyIdentifier, System.Func<System.IServiceProvider, Azure.Core.TokenCredential> tokenCredentialFactory) { throw null; }
68
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder builder, System.Uri keyIdentifier, Azure.Core.TokenCredential tokenCredential) { throw null; }
79
}
810
}

sdk/extensions/Azure.Extensions.AspNetCore.DataProtection.Keys/src/AzureDataProtectionKeyVaultKeyBuilderExtensions.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.AspNetCore.DataProtection.Internal;
1010
using Microsoft.AspNetCore.DataProtection.KeyManagement;
1111
using Microsoft.Extensions.DependencyInjection;
12+
using Microsoft.Extensions.Options;
1213

1314
#pragma warning disable AZC0001 // Extension methods have to be in the correct namespace to appear in intellisense.
1415
namespace Microsoft.AspNetCore.DataProtection
@@ -36,8 +37,8 @@ public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProt
3637
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
3738
/// </summary>
3839
/// <param name="builder">The builder instance to modify.</param>
39-
/// <param name="keyResolver">The <see cref="IKeyEncryptionKeyResolver"/> to use for Key Vault access.</param>
4040
/// <param name="keyIdentifier">The Azure Key Vault key identifier used for key encryption.</param>
41+
/// <param name="keyResolver">The <see cref="IKeyEncryptionKeyResolver"/> to use for Key Vault access.</param>
4142
/// <returns>The value <paramref name="builder"/>.</returns>
4243
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, IKeyEncryptionKeyResolver keyResolver)
4344
{
@@ -54,5 +55,58 @@ public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProt
5455

5556
return builder;
5657
}
58+
59+
/// <summary>
60+
/// Configures the data protection system to protect keys with specified key in Azure Key Vault.
61+
/// </summary>
62+
/// <param name="builder">The builder instance to modify.</param>
63+
/// <param name="keyIdentifier">The Azure Key Vault key identifier used for key encryption.</param>
64+
/// <param name="keyResolverFactory">The factory delegate to create the <see cref="IKeyEncryptionKeyResolver"/> to use for Key Vault access.</param>
65+
/// <returns>The value <paramref name="builder"/>.</returns>
66+
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, Func<IServiceProvider, IKeyEncryptionKeyResolver> keyResolverFactory)
67+
{
68+
Argument.AssertNotNull(builder, nameof(builder));
69+
Argument.AssertNotNull(keyResolverFactory, nameof(keyResolverFactory));
70+
Argument.AssertNotNullOrEmpty(keyIdentifier, nameof(keyIdentifier));
71+
72+
builder.Services.AddSingleton<IActivator, DecryptorTypeForwardingActivator>();
73+
74+
builder.Services.AddSingleton(sp =>
75+
{
76+
var keyResolver = keyResolverFactory(sp);
77+
return new AzureKeyVaultXmlEncryptor(keyResolver, keyIdentifier);
78+
});
79+
80+
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>, ConfigureKeyManagementKeyVaultEncryptorClientOptions>();
81+
82+
return builder;
83+
}
84+
85+
/// <summary>
86+
/// Configures the data protection system to protect keys with specified key in Azure Key Vault.
87+
/// </summary>
88+
/// <param name="builder">The builder instance to modify.</param>
89+
/// <param name="keyIdentifier">The Azure Key Vault key identifier used for key encryption.</param>
90+
/// <param name="tokenCredentialFactory">The factory delegate to create the <see cref="TokenCredential"/> to use for authenticating Key Vault access.</param>
91+
/// <returns>The value <paramref name="builder"/>.</returns>
92+
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, Func<IServiceProvider, TokenCredential> tokenCredentialFactory)
93+
{
94+
Argument.AssertNotNull(builder, nameof(builder));
95+
Argument.AssertNotNull(tokenCredentialFactory, nameof(tokenCredentialFactory));
96+
Argument.AssertNotNullOrEmpty(keyIdentifier, nameof(keyIdentifier));
97+
98+
builder.Services.AddSingleton<IActivator, DecryptorTypeForwardingActivator>();
99+
100+
builder.Services.AddSingleton(sp =>
101+
{
102+
var tokenCredential = tokenCredentialFactory(sp);
103+
var keyResolver = new KeyResolver(tokenCredential);
104+
return new AzureKeyVaultXmlEncryptor(keyResolver, keyIdentifier);
105+
});
106+
107+
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>, ConfigureKeyManagementKeyVaultEncryptorClientOptions>();
108+
109+
return builder;
110+
}
57111
}
58112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.AspNetCore.DataProtection.KeyManagement;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Options;
7+
8+
namespace Azure.Extensions.AspNetCore.DataProtection.Keys
9+
{
10+
internal sealed class ConfigureKeyManagementKeyVaultEncryptorClientOptions : IConfigureOptions<KeyManagementOptions>
11+
{
12+
private readonly IServiceScopeFactory _serviceScopeFactory;
13+
14+
public ConfigureKeyManagementKeyVaultEncryptorClientOptions(IServiceScopeFactory serviceScopeFactory)
15+
{
16+
_serviceScopeFactory = serviceScopeFactory;
17+
}
18+
19+
public void Configure(KeyManagementOptions options)
20+
{
21+
using var scope = _serviceScopeFactory.CreateScope();
22+
23+
var provider = scope.ServiceProvider;
24+
25+
options.XmlEncryptor = provider.GetRequiredService<AzureKeyVaultXmlEncryptor>();
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Azure.Identity;
7+
using Microsoft.AspNetCore.DataProtection;
8+
using Microsoft.AspNetCore.DataProtection.KeyManagement;
9+
using Microsoft.Azure.KeyVault;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Options;
12+
using NUnit.Framework;
13+
14+
namespace Azure.Extensions.AspNetCore.DataProtection.Keys.Tests
15+
{
16+
public class AzureDataProtectionBuilderExtensionsTests
17+
{
18+
[Test]
19+
public void ProtectKeysWithAzureKeyVault_UsesAzureKeyVaultXmlEncryptor()
20+
{
21+
// Arrange
22+
var client = new KeyVaultClient((_, _, _) => Task.FromResult(string.Empty));
23+
var serviceCollection = new ServiceCollection();
24+
var builder = serviceCollection.AddDataProtection();
25+
26+
// Act
27+
builder.ProtectKeysWithAzureKeyVault(new Uri("http://www.example.com/dummyKey"), new DefaultAzureCredential());
28+
var services = serviceCollection.BuildServiceProvider();
29+
30+
// Assert
31+
var options = services.GetRequiredService<IOptions<KeyManagementOptions>>();
32+
Assert.IsInstanceOf<AzureKeyVaultXmlEncryptor>(options.Value.XmlEncryptor);
33+
}
34+
35+
[Test]
36+
public void ProtectKeysWithAzureKeyVault_WithServiceProviderFunc_UsesAzureKeyVaultXmlEncryptor()
37+
{
38+
// Arrange
39+
var client = new KeyVaultClient((_, _, _) => Task.FromResult(string.Empty));
40+
var serviceCollection = new ServiceCollection();
41+
var builder = serviceCollection.AddDataProtection();
42+
43+
// Act
44+
builder.ProtectKeysWithAzureKeyVault("http://www.example.com/dummyKey", sp => new DefaultAzureCredential());
45+
var services = serviceCollection.BuildServiceProvider();
46+
47+
// Assert
48+
var options = services.GetRequiredService<IOptions<KeyManagementOptions>>();
49+
Assert.IsInstanceOf<AzureKeyVaultXmlEncryptor>(options.Value.XmlEncryptor);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)