From 5d1a523682c04c4381dda8a164a154d7730e5b60 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 13 Jun 2017 00:00:47 -0700 Subject: [PATCH] Make ILoggerFactory an optional service on any DI-injected services --- samples/AzureBlob/AzureBlob.csproj | 3 +- samples/AzureBlob/Program.cs | 27 ++++---- .../AzureBlob/Properties/launchSettings.json | 22 ------ .../CustomEncryptorSample.csproj | 3 +- samples/CustomEncryptorSample/Program.cs | 30 ++++----- .../Properties/launchSettings.json | 22 ------ .../KeyManagementSample.csproj | 3 +- samples/KeyManagementSample/Program.cs | 64 +++++++++--------- .../Properties/launchSettings.json | 22 ------ samples/Redis/Program.cs | 25 ++++--- samples/Redis/Properties/launchSettings.json | 22 ------ samples/Redis/Redis.csproj | 3 +- .../AuthenticatedEncryptorConfiguration.cs | 3 +- ...gCbcAuthenticatedEncryptorConfiguration.cs | 3 +- ...gGcmAuthenticatedEncryptorConfiguration.cs | 3 +- ...agedAuthenticatedEncryptorConfiguration.cs | 3 +- .../DataProtectionBuilderExtensions.cs | 13 ++-- .../DataProtectionProviderFactory.cs | 16 ----- ...taProtectionServiceCollectionExtensions.cs | 8 +-- .../EphemeralDataProtectionProvider.cs | 17 ++++- .../Internal/DataProtectionStartupFilter.cs | 5 ++ .../Internal/KeyManagementOptionsSetup.cs | 14 +++- .../KeyManagement/DefaultKeyResolver.cs | 5 ++ .../KeyManagement/KeyRingProvider.cs | 18 ++--- .../KeyManagement/XmlKeyManager.cs | 12 +++- .../RegistryPolicyResolver.cs | 14 ++-- .../TypeForwardingActivator.cs | 3 +- .../breakingchanges.netcore.json | 7 +- .../EphemeralDataProtectionProviderTests.cs | 2 +- .../Internal/KeyManagementOptionsSetupTest.cs | 3 +- .../KeyManagement/KeyRingProviderTests.cs | 9 +-- .../RegistryPolicyResolverTests.cs | 3 +- .../ServiceCollectionTests.cs | 67 +++++++++++++++++++ 33 files changed, 238 insertions(+), 236 deletions(-) delete mode 100644 samples/AzureBlob/Properties/launchSettings.json delete mode 100644 samples/CustomEncryptorSample/Properties/launchSettings.json delete mode 100644 samples/KeyManagementSample/Properties/launchSettings.json delete mode 100644 samples/Redis/Properties/launchSettings.json delete mode 100644 src/Microsoft.AspNetCore.DataProtection/DataProtectionProviderFactory.cs create mode 100644 test/Microsoft.AspNetCore.DataProtection.Test/ServiceCollectionTests.cs diff --git a/samples/AzureBlob/AzureBlob.csproj b/samples/AzureBlob/AzureBlob.csproj index 9d6881602f..899b254265 100644 --- a/samples/AzureBlob/AzureBlob.csproj +++ b/samples/AzureBlob/AzureBlob.csproj @@ -1,9 +1,10 @@ - + netcoreapp2.0 + exe diff --git a/samples/AzureBlob/Program.cs b/samples/AzureBlob/Program.cs index f0aa1efea5..cce8604648 100644 --- a/samples/AzureBlob/Program.cs +++ b/samples/AzureBlob/Program.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.WindowsAzure.Storage; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace AzureBlob { @@ -24,21 +25,19 @@ namespace AzureBlob container.CreateIfNotExistsAsync().GetAwaiter().GetResult(); // Configure + using (var services = new ServiceCollection() + .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug)) + .AddDataProtection() + .PersistKeysToAzureBlobStorage(container, "keys.xml") + .Services + .BuildServiceProvider()) + { + // Run a sample payload - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddDataProtection() - .PersistKeysToAzureBlobStorage(container, "keys.xml"); - - var services = serviceCollection.BuildServiceProvider(); - var loggerFactory = services.GetService(); - loggerFactory.AddConsole(); - - // Run a sample payload - - var protector = services.GetDataProtector("sample-purpose"); - var protectedData = protector.Protect("Hello world!"); - Console.WriteLine(protectedData); + var protector = services.GetDataProtector("sample-purpose"); + var protectedData = protector.Protect("Hello world!"); + Console.WriteLine(protectedData); + } } } } diff --git a/samples/AzureBlob/Properties/launchSettings.json b/samples/AzureBlob/Properties/launchSettings.json deleted file mode 100644 index ae9a5dab5a..0000000000 --- a/samples/AzureBlob/Properties/launchSettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:2041/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "AzureBlob": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/samples/CustomEncryptorSample/CustomEncryptorSample.csproj b/samples/CustomEncryptorSample/CustomEncryptorSample.csproj index ca8401e36d..ddd92112c2 100644 --- a/samples/CustomEncryptorSample/CustomEncryptorSample.csproj +++ b/samples/CustomEncryptorSample/CustomEncryptorSample.csproj @@ -1,9 +1,10 @@ - + net461;netcoreapp2.0 + exe diff --git a/samples/CustomEncryptorSample/Program.cs b/samples/CustomEncryptorSample/Program.cs index 89e0f82810..9079aeee3f 100644 --- a/samples/CustomEncryptorSample/Program.cs +++ b/samples/CustomEncryptorSample/Program.cs @@ -14,25 +14,23 @@ namespace CustomEncryptorSample public static void Main(string[] args) { var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys"); - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddDataProtection() + using (var services = new ServiceCollection() + .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug)) + .AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(keysFolder)) - .UseXmlEncryptor(s => new CustomXmlEncryptor(s)); + .UseXmlEncryptor(s => new CustomXmlEncryptor(s)) + .Services.BuildServiceProvider()) + { + var protector = services.GetDataProtector("SamplePurpose"); - var services = serviceCollection.BuildServiceProvider(); - var loggerFactory = services.GetRequiredService(); - loggerFactory.AddConsole(); + // protect the payload + var protectedPayload = protector.Protect("Hello World!"); + Console.WriteLine($"Protect returned: {protectedPayload}"); - var protector = services.GetDataProtector("SamplePurpose"); - - // protect the payload - var protectedPayload = protector.Protect("Hello World!"); - Console.WriteLine($"Protect returned: {protectedPayload}"); - - // unprotect the payload - var unprotectedPayload = protector.Unprotect(protectedPayload); - Console.WriteLine($"Unprotect returned: {unprotectedPayload}"); + // unprotect the payload + var unprotectedPayload = protector.Unprotect(protectedPayload); + Console.WriteLine($"Unprotect returned: {unprotectedPayload}"); + } } } } diff --git a/samples/CustomEncryptorSample/Properties/launchSettings.json b/samples/CustomEncryptorSample/Properties/launchSettings.json deleted file mode 100644 index c24bc96703..0000000000 --- a/samples/CustomEncryptorSample/Properties/launchSettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:1398/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "CustomEncryptorSample": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/samples/KeyManagementSample/KeyManagementSample.csproj b/samples/KeyManagementSample/KeyManagementSample.csproj index b15e9017a6..34b42ebd92 100644 --- a/samples/KeyManagementSample/KeyManagementSample.csproj +++ b/samples/KeyManagementSample/KeyManagementSample.csproj @@ -1,9 +1,10 @@ - + net461;netcoreapp2.0 + exe diff --git a/samples/KeyManagementSample/Program.cs b/samples/KeyManagementSample/Program.cs index 3feefebc14..be128aa11c 100644 --- a/samples/KeyManagementSample/Program.cs +++ b/samples/KeyManagementSample/Program.cs @@ -16,7 +16,8 @@ namespace KeyManagementSample { var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys"); var serviceCollection = new ServiceCollection(); - var builder = serviceCollection.AddDataProtection() + var builder = serviceCollection + .AddDataProtection() // point at a specific folder and use DPAPI to encrypt keys .PersistKeysToFileSystem(new DirectoryInfo(keysFolder)); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -24,40 +25,41 @@ namespace KeyManagementSample builder.ProtectKeysWithDpapi(); } - var services = serviceCollection.BuildServiceProvider(); - - // perform a protect operation to force the system to put at least - // one key in the key ring - services.GetDataProtector("Sample.KeyManager.v1").Protect("payload"); - Console.WriteLine("Performed a protect operation."); - - // get a reference to the key manager - var keyManager = services.GetService(); - - // list all keys in the key ring - var allKeys = keyManager.GetAllKeys(); - Console.WriteLine($"The key ring contains {allKeys.Count} key(s)."); - foreach (var key in allKeys) + using (var services = serviceCollection.BuildServiceProvider()) { - Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}"); - } + // perform a protect operation to force the system to put at least + // one key in the key ring + services.GetDataProtector("Sample.KeyManager.v1").Protect("payload"); + Console.WriteLine("Performed a protect operation."); - // revoke all keys in the key ring - keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here."); - Console.WriteLine("Revoked all existing keys."); + // get a reference to the key manager + var keyManager = services.GetService(); - // add a new key to the key ring with immediate activation and a 1-month expiration - keyManager.CreateNewKey( - activationDate: DateTimeOffset.Now, - expirationDate: DateTimeOffset.Now.AddMonths(1)); - Console.WriteLine("Added a new key."); + // list all keys in the key ring + var allKeys = keyManager.GetAllKeys(); + Console.WriteLine($"The key ring contains {allKeys.Count} key(s)."); + foreach (var key in allKeys) + { + Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}"); + } - // list all keys in the key ring - allKeys = keyManager.GetAllKeys(); - Console.WriteLine($"The key ring contains {allKeys.Count} key(s)."); - foreach (var key in allKeys) - { - Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}"); + // revoke all keys in the key ring + keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here."); + Console.WriteLine("Revoked all existing keys."); + + // add a new key to the key ring with immediate activation and a 1-month expiration + keyManager.CreateNewKey( + activationDate: DateTimeOffset.Now, + expirationDate: DateTimeOffset.Now.AddMonths(1)); + Console.WriteLine("Added a new key."); + + // list all keys in the key ring + allKeys = keyManager.GetAllKeys(); + Console.WriteLine($"The key ring contains {allKeys.Count} key(s)."); + foreach (var key in allKeys) + { + Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}"); + } } } } diff --git a/samples/KeyManagementSample/Properties/launchSettings.json b/samples/KeyManagementSample/Properties/launchSettings.json deleted file mode 100644 index 9f2e8074fe..0000000000 --- a/samples/KeyManagementSample/Properties/launchSettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:1396/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "KeyManagementSample": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/samples/Redis/Program.cs b/samples/Redis/Program.cs index 6731c10541..f8f213cfad 100644 --- a/samples/Redis/Program.cs +++ b/samples/Redis/Program.cs @@ -17,19 +17,18 @@ namespace Redis var redis = ConnectionMultiplexer.Connect("localhost:6379"); // Configure - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddDataProtection() - .PersistKeysToRedis(redis, "DataProtection-Keys"); - - var services = serviceCollection.BuildServiceProvider(); - var loggerFactory = services.GetService(); - loggerFactory.AddConsole(); - - // Run a sample payload - var protector = services.GetDataProtector("sample-purpose"); - var protectedData = protector.Protect("Hello world!"); - Console.WriteLine(protectedData); + using (var services = new ServiceCollection() + .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug)) + .AddDataProtection() + .PersistKeysToRedis(redis, "DataProtection-Keys") + .Services + .BuildServiceProvider()) + { + // Run a sample payload + var protector = services.GetDataProtector("sample-purpose"); + var protectedData = protector.Protect("Hello world!"); + Console.WriteLine(protectedData); + } } } } diff --git a/samples/Redis/Properties/launchSettings.json b/samples/Redis/Properties/launchSettings.json deleted file mode 100644 index 4f4c767916..0000000000 --- a/samples/Redis/Properties/launchSettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:2042/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Redis": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/samples/Redis/Redis.csproj b/samples/Redis/Redis.csproj index 37aecfb68b..7040d86af2 100644 --- a/samples/Redis/Redis.csproj +++ b/samples/Redis/Redis.csproj @@ -1,9 +1,10 @@ - + net461;netcoreapp2.0 + exe diff --git a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorConfiguration.cs b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorConfiguration.cs index c3972e4e61..606d7484fb 100644 --- a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorConfiguration.cs +++ b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorConfiguration.cs @@ -4,6 +4,7 @@ using System; using System.Security.Cryptography; using Microsoft.AspNetCore.Cryptography; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel { @@ -42,7 +43,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat void IInternalAlgorithmConfiguration.Validate() { - var factory = new AuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory()); + var factory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance); // Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly. var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this); try diff --git a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngCbcAuthenticatedEncryptorConfiguration.cs b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngCbcAuthenticatedEncryptorConfiguration.cs index 4b74177540..1c23957db2 100644 --- a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngCbcAuthenticatedEncryptorConfiguration.cs +++ b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngCbcAuthenticatedEncryptorConfiguration.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Cryptography; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel { @@ -88,7 +89,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat /// void IInternalAlgorithmConfiguration.Validate() { - var factory = new CngCbcAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory()); + var factory = new CngCbcAuthenticatedEncryptorFactory(NullLoggerFactory.Instance); // Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly. using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this)) { diff --git a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngGcmAuthenticatedEncryptorConfiguration.cs b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngGcmAuthenticatedEncryptorConfiguration.cs index 9cf6e95136..d9c1f84718 100644 --- a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngGcmAuthenticatedEncryptorConfiguration.cs +++ b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/CngGcmAuthenticatedEncryptorConfiguration.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Cryptography; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel { @@ -64,7 +65,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat /// void IInternalAlgorithmConfiguration.Validate() { - var factory = new CngGcmAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory()); + var factory = new CngGcmAuthenticatedEncryptorFactory(NullLoggerFactory.Instance); // Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly. using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this)) { diff --git a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs index b437d59bf2..dad6cd9dbc 100644 --- a/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs +++ b/src/Microsoft.AspNetCore.DataProtection/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs @@ -3,6 +3,7 @@ using System; using System.Security.Cryptography; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel { @@ -66,7 +67,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat /// void IInternalAlgorithmConfiguration.Validate() { - var factory = new ManagedAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory()); + var factory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance); // Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly. using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this)) { diff --git a/src/Microsoft.AspNetCore.DataProtection/DataProtectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.DataProtection/DataProtectionBuilderExtensions.cs index 0bbb916868..30b9edbf32 100644 --- a/src/Microsoft.AspNetCore.DataProtection/DataProtectionBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.DataProtection/DataProtectionBuilderExtensions.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Win32; @@ -206,7 +207,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; return new ConfigureOptions(options => { options.XmlRepository = new FileSystemXmlRepository(directory, loggerFactory); @@ -236,7 +237,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; return new ConfigureOptions(options => { options.XmlRepository = new RegistryXmlRepository(registryKey, loggerFactory); @@ -266,7 +267,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; return new ConfigureOptions(options => { options.XmlEncryptor = new CertificateXmlEncryptor(certificate, loggerFactory); @@ -306,7 +307,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; var certificateResolver = services.GetRequiredService(); return new ConfigureOptions(options => { @@ -357,7 +358,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; return new ConfigureOptions(options => { CryptoUtil.AssertPlatformIsWindows(); @@ -419,7 +420,7 @@ namespace Microsoft.AspNetCore.DataProtection builder.Services.AddSingleton>(services => { - var loggerFactory = services.GetRequiredService(); + var loggerFactory = services.GetService() ?? NullLoggerFactory.Instance; return new ConfigureOptions(options => { CryptoUtil.AssertPlatformIsWindows8OrLater(); diff --git a/src/Microsoft.AspNetCore.DataProtection/DataProtectionProviderFactory.cs b/src/Microsoft.AspNetCore.DataProtection/DataProtectionProviderFactory.cs deleted file mode 100644 index 4f05478c8d..0000000000 --- a/src/Microsoft.AspNetCore.DataProtection/DataProtectionProviderFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; - -namespace Microsoft.AspNetCore.DataProtection -{ - internal static class DataProtectionProviderFactory - { - public static ILoggerFactory GetDefaultLoggerFactory() - { - return NullLoggerFactory.Instance; - } - } -} diff --git a/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs index 0df59732cf..e951736e1f 100644 --- a/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection @@ -64,8 +65,6 @@ namespace Microsoft.Extensions.DependencyInjection private static void AddDataProtectionServices(IServiceCollection services) { - services.TryAddSingleton(DataProtectionProviderFactory.GetDefaultLoggerFactory()); - if (OSVersionUtil.IsWindows()) { services.TryAddSingleton(); @@ -88,10 +87,9 @@ namespace Microsoft.Extensions.DependencyInjection { var dpOptions = s.GetRequiredService>(); var keyRingProvider = s.GetRequiredService(); - var loggerFactory = s.GetRequiredService(); + var loggerFactory = s.GetService() ?? NullLoggerFactory.Instance; - IDataProtectionProvider dataProtectionProvider = null; - dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, loggerFactory); + IDataProtectionProvider dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, loggerFactory); // Link the provider to the supplied discriminator if (!string.IsNullOrEmpty(dpOptions.Value.ApplicationDiscriminator)) diff --git a/src/Microsoft.AspNetCore.DataProtection/EphemeralDataProtectionProvider.cs b/src/Microsoft.AspNetCore.DataProtection/EphemeralDataProtectionProvider.cs index 93cb021537..587b0ebfd4 100644 --- a/src/Microsoft.AspNetCore.DataProtection/EphemeralDataProtectionProvider.cs +++ b/src/Microsoft.AspNetCore.DataProtection/EphemeralDataProtectionProvider.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationM using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection { @@ -24,11 +25,23 @@ namespace Microsoft.AspNetCore.DataProtection private readonly KeyRingBasedDataProtectionProvider _dataProtectionProvider; /// - /// Creates an ephemeral , optionally providing - /// services (such as logging) for consumption by the provider. + /// Creates an ephemeral . /// + public EphemeralDataProtectionProvider() + : this (NullLoggerFactory.Instance) + { } + + /// + /// Creates an ephemeral with logging. + /// + /// The . public EphemeralDataProtectionProvider(ILoggerFactory loggerFactory) { + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + IKeyRingProvider keyringProvider; if (OSVersionUtil.IsWindows()) { diff --git a/src/Microsoft.AspNetCore.DataProtection/Internal/DataProtectionStartupFilter.cs b/src/Microsoft.AspNetCore.DataProtection/Internal/DataProtectionStartupFilter.cs index f2abbae5be..d9faa5b0f8 100644 --- a/src/Microsoft.AspNetCore.DataProtection/Internal/DataProtectionStartupFilter.cs +++ b/src/Microsoft.AspNetCore.DataProtection/Internal/DataProtectionStartupFilter.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection.Internal { @@ -14,6 +15,10 @@ namespace Microsoft.AspNetCore.DataProtection.Internal private readonly IKeyRingProvider _keyRingProvider; private readonly ILogger _logger; + public DataProtectionStartupFilter(IKeyRingProvider keyRingProvider) + : this(keyRingProvider, NullLoggerFactory.Instance) + { } + public DataProtectionStartupFilter(IKeyRingProvider keyRingProvider, ILoggerFactory loggerFactory) { _keyRingProvider = keyRingProvider; diff --git a/src/Microsoft.AspNetCore.DataProtection/Internal/KeyManagementOptionsSetup.cs b/src/Microsoft.AspNetCore.DataProtection/Internal/KeyManagementOptionsSetup.cs index 1f72510e09..a197b7ceba 100644 --- a/src/Microsoft.AspNetCore.DataProtection/Internal/KeyManagementOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.DataProtection/Internal/KeyManagementOptionsSetup.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.DataProtection.Internal @@ -15,7 +16,18 @@ namespace Microsoft.AspNetCore.DataProtection.Internal private readonly RegistryPolicyResolver _registryPolicyResolver; private readonly ILoggerFactory _loggerFactory; - public KeyManagementOptionsSetup(ILoggerFactory loggerFactory) : this(loggerFactory, registryPolicyResolver: null) + public KeyManagementOptionsSetup() + : this(NullLoggerFactory.Instance, registryPolicyResolver: null) + { + } + + public KeyManagementOptionsSetup(ILoggerFactory loggerFactory) + : this(loggerFactory, registryPolicyResolver: null) + { + } + + public KeyManagementOptionsSetup(RegistryPolicyResolver registryPolicyResolver) + : this(NullLoggerFactory.Instance, registryPolicyResolver) { } diff --git a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/DefaultKeyResolver.cs b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/DefaultKeyResolver.cs index 9c545c793f..b4f686c9f3 100644 --- a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/DefaultKeyResolver.cs +++ b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/DefaultKeyResolver.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.DataProtection.KeyManagement @@ -40,6 +41,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement /// private readonly TimeSpan _maxServerToServerClockSkew; + public DefaultKeyResolver(IOptions keyManagementOptions) + : this(keyManagementOptions, NullLoggerFactory.Instance) + { } + public DefaultKeyResolver(IOptions keyManagementOptions, ILoggerFactory loggerFactory) { _keyPropagationWindow = keyManagementOptions.Value.KeyPropagationWindow; diff --git a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/KeyRingProvider.cs b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/KeyRingProvider.cs index 8b0b25e7a7..e407ae62dd 100644 --- a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/KeyRingProvider.cs +++ b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/KeyRingProvider.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.DataProtection.KeyManagement @@ -16,7 +17,6 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement { private CacheableKeyRing _cacheableKeyRing; private readonly object _cacheableKeyRingLockObj = new object(); - private readonly ICacheableKeyRingProvider _cacheableKeyRingProvider; private readonly IDefaultKeyResolver _defaultKeyResolver; private readonly KeyManagementOptions _keyManagementOptions; private readonly IKeyManager _keyManager; @@ -25,31 +25,31 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement public KeyRingProvider( IKeyManager keyManager, IOptions keyManagementOptions, - IDefaultKeyResolver defaultKeyResolver, - ILoggerFactory loggerFactory) + IDefaultKeyResolver defaultKeyResolver) : this( keyManager, keyManagementOptions, - cacheableKeyRingProvider: null, - defaultKeyResolver: defaultKeyResolver, - loggerFactory: loggerFactory) + defaultKeyResolver, + NullLoggerFactory.Instance) { } public KeyRingProvider( IKeyManager keyManager, IOptions keyManagementOptions, - ICacheableKeyRingProvider cacheableKeyRingProvider, IDefaultKeyResolver defaultKeyResolver, ILoggerFactory loggerFactory) { _keyManagementOptions = new KeyManagementOptions(keyManagementOptions.Value); // clone so new instance is immutable _keyManager = keyManager; - _cacheableKeyRingProvider = cacheableKeyRingProvider ?? this; + CacheableKeyRingProvider = this; _defaultKeyResolver = defaultKeyResolver; _logger = loggerFactory.CreateLogger(); } + // for testing + internal ICacheableKeyRingProvider CacheableKeyRingProvider { get; set; } + private CacheableKeyRing CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded) { // Refresh the list of all keys @@ -183,7 +183,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement try { - newCacheableKeyRing = _cacheableKeyRingProvider.GetCacheableKeyRing(utcNow); + newCacheableKeyRing = CacheableKeyRingProvider.GetCacheableKeyRing(utcNow); } catch (Exception ex) { diff --git a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/XmlKeyManager.cs b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/XmlKeyManager.cs index b68a997c63..66e7a96dcb 100644 --- a/src/Microsoft.AspNetCore.DataProtection/KeyManagement/XmlKeyManager.cs +++ b/src/Microsoft.AspNetCore.DataProtection/KeyManagement/XmlKeyManager.cs @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.Repositories; using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Win32; @@ -55,6 +56,15 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement private CancellationTokenSource _cacheExpirationTokenSource; + /// + /// Creates an . + /// + /// The instance that provides the configuration. + /// The . + public XmlKeyManager(IOptions keyManagementOptions, IActivator activator) + : this (keyManagementOptions, activator, NullLoggerFactory.Instance) + { } + /// /// Creates an . /// @@ -63,7 +73,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement /// The . public XmlKeyManager(IOptions keyManagementOptions, IActivator activator, ILoggerFactory loggerFactory) { - _loggerFactory = loggerFactory; + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _logger = _loggerFactory.CreateLogger(); KeyRepository = keyManagementOptions.Value.XmlRepository; diff --git a/src/Microsoft.AspNetCore.DataProtection/RegistryPolicyResolver.cs b/src/Microsoft.AspNetCore.DataProtection/RegistryPolicyResolver.cs index da5b3357e6..a6f63ee9a1 100644 --- a/src/Microsoft.AspNetCore.DataProtection/RegistryPolicyResolver.cs +++ b/src/Microsoft.AspNetCore.DataProtection/RegistryPolicyResolver.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement; -using Microsoft.Extensions.Logging; using Microsoft.Win32; namespace Microsoft.AspNetCore.DataProtection @@ -22,20 +21,17 @@ namespace Microsoft.AspNetCore.DataProtection { private readonly Func _getPolicyRegKey; private readonly IActivator _activator; - private readonly ILoggerFactory _loggerFactory; - public RegistryPolicyResolver(IActivator activator, ILoggerFactory loggerFactory) + public RegistryPolicyResolver(IActivator activator) { _getPolicyRegKey = () => Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection"); _activator = activator; - _loggerFactory = loggerFactory; } - internal RegistryPolicyResolver(RegistryKey policyRegKey, IActivator activator, ILoggerFactory loggerFactory) + internal RegistryPolicyResolver(RegistryKey policyRegKey, IActivator activator) { _getPolicyRegKey = () => policyRegKey; _activator = activator; - _loggerFactory = loggerFactory; } // populates an options object from values stored in the registry @@ -95,10 +91,8 @@ namespace Microsoft.AspNetCore.DataProtection /// /// Returns a from the default registry location. /// - public static RegistryPolicy ResolveDefaultPolicy(IActivator activator, ILoggerFactory loggerFactory) - { - return new RegistryPolicyResolver(activator, loggerFactory).ResolvePolicy(); - } + public static RegistryPolicy ResolveDefaultPolicy(IActivator activator) + => new RegistryPolicyResolver(activator).ResolvePolicy(); internal RegistryPolicy ResolvePolicy() { diff --git a/src/Microsoft.AspNetCore.DataProtection/TypeForwardingActivator.cs b/src/Microsoft.AspNetCore.DataProtection/TypeForwardingActivator.cs index 311d4ed48e..bf3113eada 100644 --- a/src/Microsoft.AspNetCore.DataProtection/TypeForwardingActivator.cs +++ b/src/Microsoft.AspNetCore.DataProtection/TypeForwardingActivator.cs @@ -4,6 +4,7 @@ using System; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.DataProtection { @@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.DataProtection private static readonly Regex _versionPattern = new Regex(@",\s?Version=[0-9]+(\.[0-9]+){0,3}", RegexOptions.Compiled, TimeSpan.FromSeconds(2)); public TypeForwardingActivator(IServiceProvider services) - : this(services, DataProtectionProviderFactory.GetDefaultLoggerFactory()) + : this(services, NullLoggerFactory.Instance) { } diff --git a/src/Microsoft.AspNetCore.DataProtection/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.DataProtection/breakingchanges.netcore.json index 81b60f185d..2c1b337ca5 100644 --- a/src/Microsoft.AspNetCore.DataProtection/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.DataProtection/breakingchanges.netcore.json @@ -79,11 +79,6 @@ "MemberId": "public .ctor(Microsoft.Win32.RegistryKey registryKey, System.IServiceProvider services)", "Kind": "Removal" }, - { - "TypeId": "public sealed class Microsoft.AspNetCore.DataProtection.EphemeralDataProtectionProvider : Microsoft.AspNetCore.DataProtection.IDataProtectionProvider", - "MemberId": "public .ctor()", - "Kind": "Removal" - }, { "TypeId": "public sealed class Microsoft.AspNetCore.DataProtection.EphemeralDataProtectionProvider : Microsoft.AspNetCore.DataProtection.IDataProtectionProvider", "MemberId": "public .ctor(System.IServiceProvider services)", @@ -244,4 +239,4 @@ "MemberId": "Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.IAuthenticatedEncryptor CreateEncryptor()", "Kind": "Addition" } - ] \ No newline at end of file + ] diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/EphemeralDataProtectionProviderTests.cs b/test/Microsoft.AspNetCore.DataProtection.Test/EphemeralDataProtectionProviderTests.cs index dc7dc642f4..d42fe2113c 100644 --- a/test/Microsoft.AspNetCore.DataProtection.Test/EphemeralDataProtectionProviderTests.cs +++ b/test/Microsoft.AspNetCore.DataProtection.Test/EphemeralDataProtectionProviderTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.DataProtection public void DifferentProvider_SamePurpose_DoesNotRoundTripData() { // Arrange - var dataProtector1 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose"); + var dataProtector1 = new EphemeralDataProtectionProvider().CreateProtector("purpose"); var dataProtector2 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose"); byte[] bytes = Encoding.UTF8.GetBytes("Hello there!"); diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/Internal/KeyManagementOptionsSetupTest.cs b/test/Microsoft.AspNetCore.DataProtection.Test/Internal/KeyManagementOptionsSetupTest.cs index 6de0c19551..ae49c7edab 100644 --- a/test/Microsoft.AspNetCore.DataProtection.Test/Internal/KeyManagementOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.DataProtection.Test/Internal/KeyManagementOptionsSetupTest.cs @@ -89,8 +89,7 @@ namespace Microsoft.AspNetCore.DataProtection.Internal var policyResolver = new RegistryPolicyResolver( registryKey, - activator: SimpleActivator.DefaultWithoutServices, - loggerFactory: NullLoggerFactory.Instance); + activator: SimpleActivator.DefaultWithoutServices); var setup = new KeyManagementOptionsSetup(NullLoggerFactory.Instance, policyResolver); diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/KeyManagement/KeyRingProviderTests.cs b/test/Microsoft.AspNetCore.DataProtection.Test/KeyManagement/KeyRingProviderTests.cs index 5654943820..8582ed8359 100644 --- a/test/Microsoft.AspNetCore.DataProtection.Test/KeyManagement/KeyRingProviderTests.cs +++ b/test/Microsoft.AspNetCore.DataProtection.Test/KeyManagement/KeyRingProviderTests.cs @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement ShouldGenerateNewKey = false }) }); - + // Act var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now); @@ -597,9 +597,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement return new KeyRingProvider( keyManager: null, keyManagementOptions: Options.Create(options), - cacheableKeyRingProvider: cacheableKeyRingProvider, defaultKeyResolver: null, - loggerFactory: NullLoggerFactory.Instance); + loggerFactory: NullLoggerFactory.Instance) + { + CacheableKeyRingProvider = cacheableKeyRingProvider + }; } private static ICacheableKeyRingProvider CreateKeyRingProvider(IKeyManager keyManager, IDefaultKeyResolver defaultKeyResolver, KeyManagementOptions keyManagementOptions= null) @@ -612,7 +614,6 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement return new KeyRingProvider( keyManager: keyManager, keyManagementOptions: Options.Create(keyManagementOptions), - cacheableKeyRingProvider: null, defaultKeyResolver: defaultKeyResolver, loggerFactory: NullLoggerFactory.Instance); } diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/RegistryPolicyResolverTests.cs b/test/Microsoft.AspNetCore.DataProtection.Test/RegistryPolicyResolverTests.cs index 3ce7ee9f67..d2de2cde39 100644 --- a/test/Microsoft.AspNetCore.DataProtection.Test/RegistryPolicyResolverTests.cs +++ b/test/Microsoft.AspNetCore.DataProtection.Test/RegistryPolicyResolverTests.cs @@ -251,8 +251,7 @@ namespace Microsoft.AspNetCore.DataProtection var policyResolver = new RegistryPolicyResolver( registryKey, - activator: SimpleActivator.DefaultWithoutServices, - loggerFactory: NullLoggerFactory.Instance); + activator: SimpleActivator.DefaultWithoutServices); return policyResolver.ResolvePolicy(); }); diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/ServiceCollectionTests.cs b/test/Microsoft.AspNetCore.DataProtection.Test/ServiceCollectionTests.cs new file mode 100644 index 0000000000..ad05973c0b --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.Test/ServiceCollectionTests.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.AspNetCore.DataProtection +{ + public class ServiceCollectionTests + { + [Fact] + public void AddsOptions() + { + var services = new ServiceCollection() + .AddDataProtection() + .Services + .BuildServiceProvider(); + + Assert.NotNull(services.GetService>()); + } + + [Fact] + public void DoesNotOverrideLogging() + { + var services1 = new ServiceCollection() + .AddLogging() + .AddDataProtection() + .Services + .BuildServiceProvider(); + + var services2 = new ServiceCollection() + .AddDataProtection() + .Services + .AddLogging() + .BuildServiceProvider(); + + Assert.Equal( + services1.GetRequiredService().GetType(), + services2.GetRequiredService().GetType()); + } + + [Fact] + public void CanResolveAllRegisteredServices() + { + var serviceCollection = new ServiceCollection() + .AddDataProtection() + .Services; + var services = serviceCollection.BuildServiceProvider(validateScopes: true); + + Assert.Null(services.GetService()); + + foreach (var descriptor in serviceCollection) + { + if (descriptor.ServiceType.Assembly.GetName().Name == "Microsoft.Extensions.Options") + { + // ignore any descriptors added by the call to .AddOptions() + continue; + } + + Assert.NotNull(services.GetService(descriptor.ServiceType)); + } + } + } +}