diff --git a/DataProtection.sln b/DataProtection.sln index b98012a3..71fd7cbe 100644 --- a/DataProtection.sln +++ b/DataProtection.sln @@ -52,6 +52,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataPr EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Redis", "samples\Redis\Redis.xproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataProtection.DistributedStore", "src\Microsoft.AspNetCore.DataProtection.DistributedStore\Microsoft.AspNetCore.DataProtection.DistributedStore.xproj", "{5694BC38-B266-447E-A743-DA1139FCB257}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataProtection.DistributedStore.Test", "test\Microsoft.AspNetCore.DataProtection.DistributedStore.Test\Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj", "{FFD8D535-91EB-405F-AB80-8C2D972D5C66}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DistributedStore", "samples\DistributedStore\DistributedStore.xproj", "{CC657C4D-D281-4CB3-86E9-053D68817218}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,5 +238,8 @@ Global {ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} {8C41240E-48F8-402F-9388-74CFE27F4D76} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} {24AAEC96-DF46-4F61-B2FF-3D5E056685D9} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} + {5694BC38-B266-447E-A743-DA1139FCB257} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} + {FFD8D535-91EB-405F-AB80-8C2D972D5C66} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} + {CC657C4D-D281-4CB3-86E9-053D68817218} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2} EndGlobalSection EndGlobal diff --git a/samples/DistributedStore/DistributedStore.xproj b/samples/DistributedStore/DistributedStore.xproj new file mode 100644 index 00000000..24fb1f54 --- /dev/null +++ b/samples/DistributedStore/DistributedStore.xproj @@ -0,0 +1,18 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + cc657c4d-d281-4cb3-86e9-053d68817218 + DistributedStore + .\obj + .\bin\ + + + 2.0 + + + \ No newline at end of file diff --git a/samples/DistributedStore/Program.cs b/samples/DistributedStore/Program.cs new file mode 100644 index 00000000..c549c80c --- /dev/null +++ b/samples/DistributedStore/Program.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace DistributedStore +{ + public class Program + { + public static void Main(string[] args) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddDistributedMemoryCache(); + serviceCollection.AddDataProtection() + .PersistKeysToDistributedStore(); + + var services = serviceCollection.BuildServiceProvider(); + var loggerFactory = services.GetService(); + loggerFactory.AddConsole(LogLevel.Trace); + + var protector = services.GetDataProtector("sample-purpose"); + var protectedData = protector.Protect("Hello world!"); + Console.WriteLine($"{nameof(protectedData)}: {protectedData}"); + var unprotectedData = protector.Unprotect(protectedData); + Console.WriteLine($"{nameof(unprotectedData)}: {unprotectedData}"); + } + } +} \ No newline at end of file diff --git a/samples/DistributedStore/project.json b/samples/DistributedStore/project.json new file mode 100644 index 00000000..6db5a228 --- /dev/null +++ b/samples/DistributedStore/project.json @@ -0,0 +1,25 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "debugType": "portable", + "emitEntryPoint": true + }, + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "1.2.0-*", + "Microsoft.Extensions.Logging": "1.2.0-*", + "Microsoft.Extensions.Logging.Console": "1.2.0-*", + "Microsoft.Extensions.Caching.Memory": "1.2.0-*", + "Microsoft.AspNetCore.DataProtection.DistributedStore": "1.0.0-*" + }, + "frameworks": { + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.2.0-*", + "type": "platform" + } + } + }, + "net451": {} + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.cs new file mode 100644 index 00000000..3bd13d74 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.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 System; +using Microsoft.AspNetCore.DataProtection.Repositories; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Caching.Distributed; + +namespace Microsoft.AspNetCore.DataProtection +{ + public static class DistributedStoreDataProtectionBuilderExtensions + { + public static IDataProtectionBuilder PersistKeysToDistributedStore(this IDataProtectionBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Services.AddSingleton(); + + return builder; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs new file mode 100644 index 00000000..2b438baf --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs @@ -0,0 +1,56 @@ +// 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 System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Microsoft.AspNetCore.DataProtection.Repositories; +using Microsoft.Extensions.Caching.Distributed; +using System.Text; + +namespace Microsoft.AspNetCore.DataProtection +{ + public class DistributedStoreXmlRepository: IXmlRepository + { + private readonly IDistributedCache _cache; + private readonly string _key; + + public DistributedStoreXmlRepository( + IDistributedCache cache, + string key = "DataProtection-Keys") + { + if (cache == null) + { + throw new ArgumentNullException(nameof(cache)); + } + _cache = cache; + + _key = key; + } + + public IReadOnlyCollection GetAllElements() + { + var data = _cache.GetString(_key); + if(!string.IsNullOrEmpty(data)) + { + return XDocument.Parse(data).Root.Elements().ToList().AsReadOnly(); + } + else + { + return new List().AsReadOnly(); + } + } + + public void StoreElement(XElement element, string friendlyName) + { + var data = _cache.GetString(_key); + + XDocument doc = string.IsNullOrEmpty(data) ? + new XDocument(new XElement("keys")) : XDocument.Parse(data); + doc.Root.Add(element); + + _cache.SetString(_key, doc.ToString()); + } + } +} diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj new file mode 100644 index 00000000..533bdac8 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj @@ -0,0 +1,18 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 5694bc38-b266-447e-a743-da1139fcb257 + Microsoft.AspNetCore.DataProtection.DistributedStore + .\obj + .\bin\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e3ae91c5 --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// 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.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-US")] +[assembly: AssemblyCompany("Microsoft Corporation.")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyProduct("Microsoft ASP.NET Core")] diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json new file mode 100644 index 00000000..6cf91eda --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json @@ -0,0 +1,34 @@ +{ + "version": "1.0.0-*", + "description": "IDistributedCache as key store.", + "packOptions": { + "repository": { + "type": "git", + "url": "git://github.com/aspnet/dataprotection" + }, + "tags": [ + "aspnetcore", + "dataprotection" + ] + }, + "dependencies": { + "Microsoft.AspNetCore.DataProtection": { + "target": "project" + }, + "Microsoft.Extensions.Caching.Abstractions": "1.2.0-*", + "NETStandard.Library": "1.6.2-*" + }, + "frameworks": { + "net451": {}, + "netstandard1.5": {} + }, + "buildOptions": { + "allowUnsafe": true, + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk", + "nowarn": [ + "CS1591" + ], + "xmlDoc": true + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs new file mode 100644 index 00000000..7edbdf25 --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs @@ -0,0 +1,35 @@ +// 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.Linq; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Extensions.Caching.Distributed; +using Xunit; +using Microsoft.Extensions.Caching.Memory; + +namespace Microsoft.AspNetCore.DataProtection +{ + public class DataProtectionDistributedStoreTests + { + private DistributedStoreXmlRepository _xmlRepository; + + public DataProtectionDistributedStoreTests() + { + _xmlRepository = new DistributedStoreXmlRepository( + new MemoryDistributedCache(new MemoryCache(new MemoryCacheOptions()))); ; + } + + [Fact] + public void StoreElement_GetAllElements() + { + var element1 = XElement.Parse(""); + var element2 = XElement.Parse(""); + _xmlRepository.StoreElement(element1, null); + _xmlRepository.StoreElement(element2, null); + var actual = _xmlRepository.GetAllElements().Select(e => e.ToString()); + var expected = new XElement[] { element1, element2 }.Select(e => e.ToString()); + Assert.True(expected.SequenceEqual(actual)); + } + } +} diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj new file mode 100644 index 00000000..b5a153cb --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj @@ -0,0 +1,21 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + ffd8d535-91eb-405f-ab80-8c2d972d5c66 + Microsoft.AspNetCore.DataProtection.DistributedStore.Test + .\obj + .\bin\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json new file mode 100644 index 00000000..39250bdd --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json @@ -0,0 +1,29 @@ +{ + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.DataProtection.Abstractions": "1.2.0-*", + "Microsoft.AspNetCore.DataProtection.DistributedStore": "1.0.0-*", + "Microsoft.AspNetCore.Testing": "1.2.0-*", + "Microsoft.Extensions.Caching.Memory": "1.2.0-*", + "xunit": "2.2.0-*" + }, + "frameworks": { + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.2.0-*", + "type": "platform" + } + } + }, + "net451": {} + }, + "testRunner": "xunit", + "buildOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk", + "compile": { + "include": "../common/**/*.cs" + } + } +} \ No newline at end of file