diff --git a/DataProtection.sln b/DataProtection.sln index c08ab6a1ce..7b22058b82 100644 --- a/DataProtection.sln +++ b/DataProtection.sln @@ -77,6 +77,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureKeyVault", "samples\Az EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test", "test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test.csproj", "{C85ED942-8121-453F-8308-9DB730843B63}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test", "test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj", "{06728BF2-C5EB-44C7-9F30-14FAA5649E14}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore", "samples\EntityFrameworkCore\EntityFrameworkCore.csproj", "{E837A2E3-FC93-494C-8689-5AF9C6802AD7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore", "src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj", "{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -265,6 +271,30 @@ Global {C85ED942-8121-453F-8308-9DB730843B63}.Release|Any CPU.Build.0 = Release|Any CPU {C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.ActiveCfg = Release|Any CPU {C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.Build.0 = Release|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.ActiveCfg = Debug|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.Build.0 = Debug|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.Build.0 = Release|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.ActiveCfg = Release|Any CPU + {06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.Build.0 = Release|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|x86.ActiveCfg = Debug|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Debug|x86.Build.0 = Debug|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|Any CPU.Build.0 = Release|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|x86.ActiveCfg = Release|Any CPU + {E837A2E3-FC93-494C-8689-5AF9C6802AD7}.Release|x86.Build.0 = Release|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.Build.0 = Debug|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.Build.0 = Release|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.ActiveCfg = Release|Any CPU + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -293,6 +323,9 @@ Global {4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} {295E8539-5450-4764-B3F5-51F968628022} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} {C85ED942-8121-453F-8308-9DB730843B63} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} + {06728BF2-C5EB-44C7-9F30-14FAA5649E14} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} + {E837A2E3-FC93-494C-8689-5AF9C6802AD7} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} + {3E4CA7FE-741B-4C78-A775-220E0E3C1B03} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DD305D75-BD1B-43AE-BF04-869DA6A0858F} diff --git a/build/dependencies.props b/build/dependencies.props index 9798c889a4..06b0858ebe 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -8,6 +8,8 @@ 2.2.0-preview1-34967 2.2.0-preview1-34967 2.3.2 + 2.2.0-preview1-34967 + 2.2.0-preview1-34967 2.2.0-preview1-34967 2.2.0-preview1-34967 2.2.0-preview1-34967 diff --git a/samples/EntityFrameworkCore/DataProtectionKeyContext.cs b/samples/EntityFrameworkCore/DataProtectionKeyContext.cs new file mode 100644 index 0000000000..a84031ae50 --- /dev/null +++ b/samples/EntityFrameworkCore/DataProtectionKeyContext.cs @@ -0,0 +1,21 @@ +// 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.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace EntityFrameworkCore +{ + class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext + { + public DataProtectionKeyContext(DbContextOptions options) : base(options) { } + public DbSet DataProtectionKeys { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseInMemoryDatabase("DataProtection_EntityFrameworkCore"); + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + optionsBuilder.EnableSensitiveDataLogging(); + } + } +} diff --git a/samples/EntityFrameworkCore/EntityFrameworkCore.csproj b/samples/EntityFrameworkCore/EntityFrameworkCore.csproj new file mode 100644 index 0000000000..212ec9d566 --- /dev/null +++ b/samples/EntityFrameworkCore/EntityFrameworkCore.csproj @@ -0,0 +1,18 @@ + + + + exe + net461;netcoreapp2.1 + + + + + + + + + + + + + diff --git a/samples/EntityFrameworkCore/Program.cs b/samples/EntityFrameworkCore/Program.cs new file mode 100644 index 0000000000..9e8a0d5ee1 --- /dev/null +++ b/samples/EntityFrameworkCore/Program.cs @@ -0,0 +1,33 @@ +// 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.AspNetCore.DataProtection; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; + +namespace EntityFrameworkCore +{ + class Program + { + static void Main(string[] args) + { + // Configure + using (var services = new ServiceCollection() + .AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug)) + .AddDbContext() + .AddDataProtection() + .PersistKeysToDbContext() + .SetDefaultKeyLifetime(TimeSpan.FromDays(7)) + .Services + .BuildServiceProvider(validateScopes: true)) + { + // Run a sample payload + var protector = services.GetDataProtector("sample-purpose"); + var protectedData = protector.Protect("Hello world!"); + Console.WriteLine(protectedData); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs new file mode 100644 index 0000000000..246aa3c1e5 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/ConfigureKeyManagementOptions.cs @@ -0,0 +1,22 @@ +// 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.AspNetCore.DataProtection.KeyManagement; +using Microsoft.AspNetCore.DataProtection.Repositories; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +{ + internal class ConfigureKeyManagementOptions : IConfigureOptions + { + private readonly IServiceProvider _serviceProvider; + + public ConfigureKeyManagementOptions(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider; + + public void Configure(KeyManagementOptions options) + => options.XmlRepository = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs new file mode 100644 index 0000000000..b13a9fbd60 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/DataProtectionKey.cs @@ -0,0 +1,31 @@ +// 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.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +{ + /// + /// Code first model used by . + /// + public class DataProtectionKey + { + /// + /// The entity identifier of the . + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + /// + /// The friendly name of the . + /// + public string FriendlyName { get; set; } + + /// + /// The XML representation of the . + /// + public string Xml { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs new file mode 100644 index 0000000000..a3577f0e6d --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreDataProtectionExtensions.cs @@ -0,0 +1,46 @@ +// 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.AspNetCore.DataProtection.KeyManagement; +using Microsoft.AspNetCore.DataProtection.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +{ + /// + /// Extension method class for configuring instances of + /// + public static class EntityFrameworkCoreDataProtectionExtensions + { + /// + /// Configures the data protection system to persist keys to an EntityFrameworkCore datastore + /// + /// The instance to modify. + /// The value . + public static IDataProtectionBuilder PersistKeysToDbContext(this IDataProtectionBuilder builder) + where TContext : DbContext, IDataProtectionKeyContext + { + var services = builder.Services; + + services.AddScoped>( + provider => new Func( + () => provider.CreateScope().ServiceProvider.GetService())); + + services.AddScoped(provider => + { + var scope = provider.CreateScope(); + return new EntityFrameworkCoreXmlRepository( + contextFactory: scope.ServiceProvider.GetRequiredService>(), + loggerFactory: scope.ServiceProvider.GetService()); + }); + + services.AddTransient, ConfigureKeyManagementOptions>(); + + return builder; + } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs new file mode 100644 index 0000000000..6720c400b8 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/EntityFrameworkCoreXmlRepository.cs @@ -0,0 +1,69 @@ +// 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.AspNetCore.DataProtection.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +{ + /// + /// An backed by an EntityFrameworkCore datastore. + /// + public class EntityFrameworkCoreXmlRepository : IXmlRepository + where TContext : DbContext, IDataProtectionKeyContext + { + private readonly ILoggerFactory _loggerFactory; + private readonly Func _contextFactory; + + private ILogger> _logger => _loggerFactory?.CreateLogger>(); + + private TContext _context => _contextFactory?.Invoke(); + + /// + /// Creates a new instance of the . + /// + /// The factory method that creates a context to store instances of + /// The . + public EntityFrameworkCoreXmlRepository(Func contextFactory, ILoggerFactory loggerFactory = null) + { + _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); + _loggerFactory = loggerFactory; + } + + /// + public virtual IReadOnlyCollection GetAllElements() + => _context?.Set()?.AsNoTracking().Select(key => TryParseKeyXml(key.Xml)).ToList().AsReadOnly(); + + /// + public void StoreElement(XElement element, string friendlyName) + { + var newKey = new DataProtectionKey() + { + FriendlyName = friendlyName, + Xml = element.ToString(SaveOptions.DisableFormatting) + }; + var context = _context; + context?.Set()?.Add(newKey); + _logger?.LogSavingKeyToDbContext(friendlyName, typeof(TContext).Name); + context?.SaveChanges(); + } + + private XElement TryParseKeyXml(string xml) + { + try + { + return XElement.Parse(xml); + } + catch (Exception e) + { + _logger?.LogExceptionWhileParsingKeyXml(xml, e); + return null; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs new file mode 100644 index 0000000000..39998d2a79 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/IDataProtectionKeyContext.cs @@ -0,0 +1,18 @@ +// 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.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore +{ + /// + /// Interface used to store instances of in a + /// + public interface IDataProtectionKeyContext + { + /// + /// A collection of + /// + DbSet DataProtectionKeys { get; } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs new file mode 100644 index 0000000000..d0aeb09271 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/LoggingExtensions.cs @@ -0,0 +1,31 @@ +// 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; + +namespace Microsoft.Extensions.Logging +{ + internal static class LoggingExtensions + { + private static readonly Action _anExceptionOccurredWhileParsingKeyXml; + private static readonly Action _savingKeyToDbContext; + + static LoggingExtensions() + { + _anExceptionOccurredWhileParsingKeyXml = LoggerMessage.Define( + eventId: 1, + logLevel: LogLevel.Warning, + formatString: "An exception occurred while parsing the key xml '{Xml}'."); + _savingKeyToDbContext = LoggerMessage.Define( + eventId: 2, + logLevel: LogLevel.Debug, + formatString: "Saving key '{FriendlyName}' to '{DbContext}'."); + } + + public static void LogExceptionWhileParsingKeyXml(this ILogger logger, string keyXml, Exception exception) + => _anExceptionOccurredWhileParsingKeyXml(logger, keyXml, exception); + + public static void LogSavingKeyToDbContext(this ILogger logger, string friendlyName, string contextName) + => _savingKeyToDbContext(logger, friendlyName, contextName, null); + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..966b1bfc78 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj @@ -0,0 +1,24 @@ + + + + EntityFramworkCore storage support as key store. + $(ExperimentalVersionPrefix) + $(ExperimentalVersionSuffix) + false + $(ExperimentalPackageVersion) + netstandard2.0 + true + true + aspnetcore;dataprotection;entityframeworkcore + false + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs new file mode 100644 index 0000000000..31034c7f4c --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionEntityFrameworkTests.cs @@ -0,0 +1,68 @@ +// 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.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Xml.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.DataProtection +{ + public class DataProtectionEntityFrameworkTests + { + [Fact] + public void CreateRepository_ThrowsIf_ContextIsNull() + { + Assert.Throws(() => new EntityFrameworkCoreXmlRepository(null)); + } + + [Fact] + public void StoreElement_PersistsData() + { + var element = XElement.Parse(""); + var friendlyName = "Element1"; + var key = new DataProtectionKey() { FriendlyName = friendlyName, Xml = element.ToString() }; + using (var context = BuildDataProtectionKeyContext(nameof(StoreElement_PersistsData))) + { + var service = new EntityFrameworkCoreXmlRepository(() => context); + service.StoreElement(element, friendlyName); + } + // Use a separate instance of the context to verify correct data was saved to database + using (var context = BuildDataProtectionKeyContext(nameof(StoreElement_PersistsData))) + { + Assert.Equal(1, context.DataProtectionKeys.Count()); + Assert.Equal(key.FriendlyName, context.DataProtectionKeys.Single()?.FriendlyName); + Assert.Equal(key.Xml, context.DataProtectionKeys.Single()?.Xml); + } + } + + [Fact] + public void GetAllElements_ReturnsAllElements() + { + var element1 = XElement.Parse(""); + var element2 = XElement.Parse(""); + using (var context = BuildDataProtectionKeyContext(nameof(GetAllElements_ReturnsAllElements))) + { + var service = new EntityFrameworkCoreXmlRepository(() => context); + service.StoreElement(element1, "element1"); + service.StoreElement(element2, "element2"); + } + // Use a separate instance of the context to verify correct data was saved to database + using (var context = BuildDataProtectionKeyContext(nameof(GetAllElements_ReturnsAllElements))) + { + var service = new EntityFrameworkCoreXmlRepository(() => context); + var elements = service.GetAllElements(); + Assert.Equal(2, elements.Count); + } + } + + private DbContextOptions BuildDbContextOptions(string databaseName) + => new DbContextOptionsBuilder().UseInMemoryDatabase(databaseName: databaseName).Options; + + private DataProtectionKeyContext BuildDataProtectionKeyContext(string databaseName) + => new DataProtectionKeyContext(BuildDbContextOptions(databaseName)); + } +} diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs new file mode 100644 index 0000000000..96151de0bb --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/DataProtectionKeyContext.cs @@ -0,0 +1,14 @@ +// 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.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test +{ + class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext + { + public DataProtectionKeyContext(DbContextOptions options) : base(options) { } + + public DbSet DataProtectionKeys { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs new file mode 100644 index 0000000000..d04ccdde88 --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/EntityFrameworkCoreDataProtectionBuilderExtensionsTests.cs @@ -0,0 +1,26 @@ +// 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.AspNetCore.DataProtection.KeyManagement; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test +{ + public class EntityFrameworkCoreDataProtectionBuilderExtensionsTests + { + [Fact] + public void PersistKeysToEntityFrameworkCore_UsesEntityFrameworkCoreXmlRepository() + { + var serviceCollection = new ServiceCollection(); + serviceCollection + .AddDbContext() + .AddDataProtection() + .PersistKeysToDbContext(); + var serviceProvider = serviceCollection.BuildServiceProvider(); + var keyManagementOptions = serviceProvider.GetRequiredService>(); + Assert.IsType>(keyManagementOptions.Value.XmlRepository); + } + } +} diff --git a/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj new file mode 100644 index 0000000000..ed07b79f25 --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj @@ -0,0 +1,15 @@ + + + + $(StandardTestTfms) + + + + + + + + + + +