From 3192da444333cfbc30e4aff33a748dea76363756 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 9 Jul 2020 17:10:10 -0700 Subject: [PATCH] Protected Browser Storage (#23553) * Migrated protected browser storage. * Added E2E tests. * Added safeguard against using ProtectedBrowserStorage in wasm. * Added TryGetValue. * Added Microsoft.AspNetCore.Components.Web.Extensions * Minor cleanup * Moved ProtectedBrowserStorage out of Web.JS. * Delete Microsoft.AspNetCore.Components.Web.Extensions.netcoreapp.cs * Updated ProjectReferences.props * Improvements and cleanup. * Update Microsoft.AspNetCore.Components.Web.Extensions.csproj * Added Web.Extensions to the VS solution. --- AspNetCore.sln | 242 ++++++----- eng/ProjectReferences.props | 1 + src/Components/Components.slnf | 2 + .../Components/src/Properties/AssemblyInfo.cs | 1 + src/Components/ComponentsNoDeps.slnf | 2 + ...spNetCore.Components.Web.Extensions.csproj | 20 + .../ProtectedBrowserStorage.cs | 170 ++++++++ .../ProtectedBrowserStorageResult.cs | 28 ++ ...owserStorageServiceCollectionExtensions.cs | 24 ++ .../ProtectedLocalStorage.cs | 30 ++ .../ProtectedSessionStorage.cs | 30 ++ ...ore.Components.Web.Extensions.Tests.csproj | 19 + .../test/ProtectedBrowserStorageTest.cs | 386 ++++++++++++++++++ .../ProtectedBrowserStorageUsageTest.cs | 144 +++++++ .../ProtectedBrowserStorageInjectionTest.cs | 71 ++++ .../BasicTestApp/BasicTestApp.csproj | 1 + .../test/testassets/BasicTestApp/Index.razor | 2 + .../test/testassets/BasicTestApp/Program.cs | 7 +- ...ctedBrowserStorageInjectionComponent.razor | 29 ++ ...rotectedBrowserStorageUsageComponent.razor | 56 +++ .../TestServer/Components.TestServer.csproj | 1 + .../testassets/TestServer/ServerStartup.cs | 1 + 22 files changed, 1163 insertions(+), 104 deletions(-) create mode 100644 src/Components/Web.Extensions/src/Microsoft.AspNetCore.Components.Web.Extensions.csproj create mode 100644 src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorage.cs create mode 100644 src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageResult.cs create mode 100644 src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageServiceCollectionExtensions.cs create mode 100644 src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedLocalStorage.cs create mode 100644 src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedSessionStorage.cs create mode 100644 src/Components/Web.Extensions/test/Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj create mode 100644 src/Components/Web.Extensions/test/ProtectedBrowserStorageTest.cs create mode 100644 src/Components/test/E2ETest/ServerExecutionTests/ProtectedBrowserStorageUsageTest.cs create mode 100644 src/Components/test/E2ETest/Tests/ProtectedBrowserStorageInjectionTest.cs create mode 100644 src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageInjectionComponent.razor create mode 100644 src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageUsageComponent.razor diff --git a/AspNetCore.sln b/AspNetCore.sln index 93e4a9ed0c..ca9dfcf281 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30204.135 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{C28A32F6-8314-412E-9F3B-CBD31C23E878}" EndProject @@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc", "Mvc", "{1A0EFF9F-E69 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc", "Mvc", "{819B136D-B8B6-46AE-8C4F-5469BBBFC46B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc", "src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj", "{D4FBDF11-7A65-4205-8AF6-ABC190EFCF50}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc", "src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj", "{D4FBDF11-7A65-4205-8AF6-ABC190EFCF50}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SignalR", "SignalR", "{5BC5A805-DCA0-41DF-91B8-520B5DAD57DA}" EndProject @@ -25,27 +25,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "server", "server", "{2C0222 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SignalR", "SignalR", "{B6081DA9-738E-4088-92DD-7FFA200523C9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR", "src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj", "{BE70E100-E6C4-4686-8592-73E2A04E877F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR", "src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj", "{BE70E100-E6C4-4686-8592-73E2A04E877F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Components", "Components", "{60D51C98-2CC0-40DF-B338-44154EFEE2FF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{6DE916F5-E78F-446C-89CB-9240AED2A2E2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Server", "src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj", "{7546E2DD-2CF4-4240-8045-2533DF539458}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Server", "src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj", "{7546E2DD-2CF4-4240-8045-2533DF539458}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Antiforgery", "Antiforgery", "{B55A5DE1-5AF3-4B18-AF04-C1735B071DA6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Antiforgery", "src\Antiforgery\src\Microsoft.AspNetCore.Antiforgery.csproj", "{210B7FF8-ADD2-4E11-831A-41B28F423E44}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Antiforgery", "src\Antiforgery\src\Microsoft.AspNetCore.Antiforgery.csproj", "{210B7FF8-ADD2-4E11-831A-41B28F423E44}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http", "Http", "{627BE8B3-59E6-4F1D-8C9C-76B804D41724}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authentication.Abstractions", "Authentication.Abstractions", "{AB0ED518-C27C-42D9-8D66-5F974033F855}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Authentication.Abstractions", "src\Http\Authentication.Abstractions\src\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "{18E65289-27FC-4EC6-BFE8-9FA852381319}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Abstractions", "src\Http\Authentication.Abstractions\src\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "{18E65289-27FC-4EC6-BFE8-9FA852381319}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authentication.Core", "Authentication.Core", "{5CF8E05A-C8DD-4551-8542-844A217B39F2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Authentication.Core", "src\Http\Authentication.Core\src\Microsoft.AspNetCore.Authentication.Core.csproj", "{74CF6CCE-B922-4FEF-87AA-38C85D42A4A2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Core", "src\Http\Authentication.Core\src\Microsoft.AspNetCore.Authentication.Core.csproj", "{74CF6CCE-B922-4FEF-87AA-38C85D42A4A2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security", "Security", "{A39335E5-7A26-4CFD-BF3B-2CFE75113498}" EndProject @@ -53,115 +53,115 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authorization", "Authorizat EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{EA034D9C-3281-426B-A363-FF1C05D12483}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Authorization", "src\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj", "{C2751270-6970-4CC8-8D8C-81A4B8D26446}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization", "src\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj", "{C2751270-6970-4CC8-8D8C-81A4B8D26446}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Policy", "Policy", "{C63C5E92-1511-45A5-A160-0FCC9B3E3CDB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Authorization.Policy", "src\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj", "{E2083951-98E7-4C1C-AB3A-935EA6311018}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization.Policy", "src\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj", "{E2083951-98E7-4C1C-AB3A-935EA6311018}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Components", "Components", "{B59D558B-AC58-4BA4-B5AF-AE51C8531B28}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components", "src\Components\Components\src\Microsoft.AspNetCore.Components.csproj", "{A345753F-B4FA-43E4-8275-151FA5B70728}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components", "src\Components\Components\src\Microsoft.AspNetCore.Components.csproj", "{A345753F-B4FA-43E4-8275-151FA5B70728}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{08B240FC-AE5D-478A-963D-890ADE70B0B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Web", "src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj", "{34A797ED-AC07-476D-BEB1-E86DBBF2395A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web", "src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj", "{34A797ED-AC07-476D-BEB1-E86DBBF2395A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Servers", "Servers", "{0ACCEDA7-339C-4B4D-8DD4-1AC271F31C04}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Connections.Abstractions", "Connections.Abstractions", "{78DD1AC3-0981-4109-861B-C3E2F1FC3281}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Connections.Abstractions", "src\Servers\Connections.Abstractions\src\Microsoft.AspNetCore.Connections.Abstractions.csproj", "{D5D2C620-42B7-4ECF-A892-295BB124A833}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Connections.Abstractions", "src\Servers\Connections.Abstractions\src\Microsoft.AspNetCore.Connections.Abstractions.csproj", "{D5D2C620-42B7-4ECF-A892-295BB124A833}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Middleware", "Middleware", "{E5963C9F-20A6-4385-B364-814D2581FADF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CORS", "CORS", "{27223011-E8E9-4178-9377-44A7B3CBCC21}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cors", "src\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj", "{8CDA0BDC-05C2-451F-9990-2BEF471684BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cors", "src\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj", "{8CDA0BDC-05C2-451F-9990-2BEF471684BF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{380430A1-C02F-4943-8A52-FDFA9F7735F0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cryptography.Internal", "Cryptography.Internal", "{5B27FAB7-E5FE-4FF2-8F39-900E177D5E39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{392701C6-0BDB-4505-ADB8-1475770A2421}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{392701C6-0BDB-4505-ADB8-1475770A2421}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{682157DB-17F2-40D6-B0ED-11B6C6CB064B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection", "src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj", "{DAF8C29D-5FAE-414F-8630-43CA8ECDCCD1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection", "src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj", "{DAF8C29D-5FAE-414F-8630-43CA8ECDCCD1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{64D54BBB-13D1-4655-A149-CFB754820BD2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{5BF0F5D9-8502-4431-BFFD-CA8B9D21142A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{5BF0F5D9-8502-4431-BFFD-CA8B9D21142A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics.Abstractions", "Diagnostics.Abstractions", "{ECB76EBA-0512-4449-95AA-7258F543BA2E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.Abstractions", "src\Middleware\Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj", "{1D6348B5-0616-41A6-90ED-09D34C452926}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Diagnostics.Abstractions", "src\Middleware\Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj", "{1D6348B5-0616-41A6-90ED-09D34C452926}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosting", "Hosting", "{2DB7E3C7-D213-4609-95DD-72561F23AB58}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{83C6944F-8F6C-4698-A09A-25DB92BA1AD2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Hosting.Abstractions", "src\Hosting\Abstractions\src\Microsoft.AspNetCore.Hosting.Abstractions.csproj", "{456056B5-3888-47AB-AFD7-4F16A8A1454F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Hosting.Abstractions", "src\Hosting\Abstractions\src\Microsoft.AspNetCore.Hosting.Abstractions.csproj", "{456056B5-3888-47AB-AFD7-4F16A8A1454F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server.Abstractions", "Server.Abstractions", "{44663691-023E-48D0-9757-A106974E8AAE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Hosting.Server.Abstractions", "src\Hosting\Server.Abstractions\src\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj", "{4DE12851-6ABC-4675-84B8-0E0EFA142F79}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Hosting.Server.Abstractions", "src\Hosting\Server.Abstractions\src\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj", "{4DE12851-6ABC-4675-84B8-0E0EFA142F79}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Html", "Html", "{13E7C397-F40B-4791-B853-F7430AF88D00}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{412D4C15-F48F-4DB1-940A-131D1AA87088}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Html.Abstractions", "src\Html\Abstractions\src\Microsoft.AspNetCore.Html.Abstractions.csproj", "{A400C938-46E2-4B84-B06E-487B13D22E1B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Html.Abstractions", "src\Html\Abstractions\src\Microsoft.AspNetCore.Html.Abstractions.csproj", "{A400C938-46E2-4B84-B06E-487B13D22E1B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http", "Http", "{3D330C1E-D773-4BA8-9E91-123544B0D672}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http", "src\Http\Http\src\Microsoft.AspNetCore.Http.csproj", "{563714CA-0022-4A95-BDBB-0953541193CA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http", "src\Http\Http\src\Microsoft.AspNetCore.Http.csproj", "{563714CA-0022-4A95-BDBB-0953541193CA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Abstractions", "Http.Abstractions", "{DCBBDB52-4A49-4141-8F4D-81C0FFFB7BD5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Abstractions", "src\Http\Http.Abstractions\src\Microsoft.AspNetCore.Http.Abstractions.csproj", "{E55659A7-5489-4E86-B11C-2E41697555F7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Abstractions", "src\Http\Http.Abstractions\src\Microsoft.AspNetCore.Http.Abstractions.csproj", "{E55659A7-5489-4E86-B11C-2E41697555F7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{1A304CA0-7795-4684-88E5-E66402966927}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Connections", "Http.Connections", "{A0FFAF00-D628-4BDB-BE68-8DBA21FBBEA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Connections", "src\SignalR\common\Http.Connections\src\Microsoft.AspNetCore.Http.Connections.csproj", "{627F94BE-AD74-4188-9F61-12E5096DA826}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Connections", "src\SignalR\common\Http.Connections\src\Microsoft.AspNetCore.Http.Connections.csproj", "{627F94BE-AD74-4188-9F61-12E5096DA826}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Connections.Common", "Http.Connections.Common", "{BC0E5F62-06EE-407D-83C6-490785E9A011}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Connections.Common", "src\SignalR\common\Http.Connections.Common\src\Microsoft.AspNetCore.Http.Connections.Common.csproj", "{F5EAE3E3-951B-46B7-A6B0-0BC76CCB83C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Connections.Common", "src\SignalR\common\Http.Connections.Common\src\Microsoft.AspNetCore.Http.Connections.Common.csproj", "{F5EAE3E3-951B-46B7-A6B0-0BC76CCB83C5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Extensions", "Http.Extensions", "{225AEDCF-7162-4A86-AC74-06B84660B379}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Extensions", "src\Http\Http.Extensions\src\Microsoft.AspNetCore.Http.Extensions.csproj", "{606C2615-3E08-4A08-993F-FBD90F4CD021}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Extensions", "src\Http\Http.Extensions\src\Microsoft.AspNetCore.Http.Extensions.csproj", "{606C2615-3E08-4A08-993F-FBD90F4CD021}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Features", "Http.Features", "{BFDD5ACE-A1F0-4C33-8DA1-06CB5CA53177}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Features", "src\Http\Http.Features\src\Microsoft.AspNetCore.Http.Features.csproj", "{62043A86-BC6F-432E-9AFD-F3B616192D8D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Features", "src\Http\Http.Features\src\Microsoft.AspNetCore.Http.Features.csproj", "{62043A86-BC6F-432E-9AFD-F3B616192D8D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Localization", "Localization", "{9934060B-1957-40FF-83E1-5C15344E7A33}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Localization", "src\Middleware\Localization\src\Microsoft.AspNetCore.Localization.csproj", "{EEC636DE-4C4D-4F16-9D57-0AD1762CCB4D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Localization", "src\Middleware\Localization\src\Microsoft.AspNetCore.Localization.csproj", "{EEC636DE-4C4D-4F16-9D57-0AD1762CCB4D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Abstractions", "Mvc.Abstractions", "{D685AF51-2CE2-4CD8-86AA-FB0EDD47533F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Abstractions", "src\Mvc\Mvc.Abstractions\src\Microsoft.AspNetCore.Mvc.Abstractions.csproj", "{600CB408-C94D-4F69-83DB-D99EF6DAE0E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Abstractions", "src\Mvc\Mvc.Abstractions\src\Microsoft.AspNetCore.Mvc.Abstractions.csproj", "{600CB408-C94D-4F69-83DB-D99EF6DAE0E1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.ApiExplorer", "Mvc.ApiExplorer", "{0322CF4A-9701-4E7E-9827-BC7DFFA78842}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.ApiExplorer", "src\Mvc\Mvc.ApiExplorer\src\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj", "{3AEEC6C0-65ED-43D1-BA42-F7E6913BB69A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.ApiExplorer", "src\Mvc\Mvc.ApiExplorer\src\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj", "{3AEEC6C0-65ED-43D1-BA42-F7E6913BB69A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Core", "Mvc.Core", "{5F51EB21-20E2-4793-A137-2E18D50696C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Core", "src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj", "{9CE2DD5F-BC82-4EEB-BF7B-70E75677566A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Core", "src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj", "{9CE2DD5F-BC82-4EEB-BF7B-70E75677566A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Cors", "Mvc.Cors", "{7ACD11D4-4682-4936-AFF6-DAC64C636612}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Cors", "src\Mvc\Mvc.Cors\src\Microsoft.AspNetCore.Mvc.Cors.csproj", "{E071FB7E-E650-44B0-9529-D5D1685AEA62}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Cors", "src\Mvc\Mvc.Cors\src\Microsoft.AspNetCore.Mvc.Cors.csproj", "{E071FB7E-E650-44B0-9529-D5D1685AEA62}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.DataAnnotations", "Mvc.DataAnnotations", "{79C7F0D4-7B1A-46BF-8932-5C2E971C5925}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.DataAnnotations", "src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj", "{6A31248F-5F64-4A73-856C-8DC6492E6AC7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.DataAnnotations", "src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj", "{6A31248F-5F64-4A73-856C-8DC6492E6AC7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Formatters.Json", "Mvc.Formatters.Json", "{644AAC18-8E13-4392-8891-3814E355C819}" EndProject @@ -169,73 +169,73 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Fo EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Localization", "Mvc.Localization", "{70AD2DB0-47D7-492F-817A-34BCAFD861C4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Localization", "src\Mvc\Mvc.Localization\src\Microsoft.AspNetCore.Mvc.Localization.csproj", "{51D0FA50-6970-4C19-B31B-3D6CCDF6C028}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Localization", "src\Mvc\Mvc.Localization\src\Microsoft.AspNetCore.Mvc.Localization.csproj", "{51D0FA50-6970-4C19-B31B-3D6CCDF6C028}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Razor", "Mvc.Razor", "{D21F1804-4690-4B00-BC6A-2147C14BF1B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Razor", "src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj", "{451C6023-83D5-4809-9F71-6A23ED725A03}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Razor", "src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj", "{451C6023-83D5-4809-9F71-6A23ED725A03}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.RazorPages", "Mvc.RazorPages", "{9578BB4F-D805-4581-9AAE-A66002BFDA1F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.RazorPages", "src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj", "{A47371F8-F47E-4AF3-9F2E-068F28CE6587}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.RazorPages", "src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj", "{A47371F8-F47E-4AF3-9F2E-068F28CE6587}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.TagHelpers", "Mvc.TagHelpers", "{3691F0FE-6A8D-4F99-95EB-2D3AD3F9B712}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.TagHelpers", "src\Mvc\Mvc.TagHelpers\src\Microsoft.AspNetCore.Mvc.TagHelpers.csproj", "{4C056678-59A0-4D37-BE70-B98D764C7EA4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.TagHelpers", "src\Mvc\Mvc.TagHelpers\src\Microsoft.AspNetCore.Mvc.TagHelpers.csproj", "{4C056678-59A0-4D37-BE70-B98D764C7EA4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.ViewFeatures", "Mvc.ViewFeatures", "{DA5E4C5A-E62C-47CD-9557-65F747638BAD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.ViewFeatures", "src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj", "{81A35CF9-214F-4351-9561-334F32B41C3A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.ViewFeatures", "src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj", "{81A35CF9-214F-4351-9561-334F32B41C3A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Razor", "Razor", "{B27FBAC2-ADA3-4A05-B232-64011B6B2DA3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Razor", "Razor", "{66A0F71A-FFB5-4763-913E-FB4A4F17EAA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Razor", "src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj", "{50D97D17-79CF-4917-AD70-F79146FE8808}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor", "src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj", "{50D97D17-79CF-4917-AD70-F79146FE8808}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Razor.Runtime", "Razor.Runtime", "{AB316CF4-D9C3-427E-8460-7C5235BED45E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Razor.Runtime", "src\Razor\Razor.Runtime\src\Microsoft.AspNetCore.Razor.Runtime.csproj", "{135B4B7B-D641-4A45-87F4-15C6A8CAA7F5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Runtime", "src\Razor\Razor.Runtime\src\Microsoft.AspNetCore.Razor.Runtime.csproj", "{135B4B7B-D641-4A45-87F4-15C6A8CAA7F5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResponseCaching.Abstractions", "ResponseCaching.Abstractions", "{0408482E-7934-49CA-A590-7E21AF4BE1BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.ResponseCaching.Abstractions", "src\Middleware\ResponseCaching.Abstractions\src\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj", "{A9027A02-135B-4149-947A-79F5B73A7F28}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ResponseCaching.Abstractions", "src\Middleware\ResponseCaching.Abstractions\src\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj", "{A9027A02-135B-4149-947A-79F5B73A7F28}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Routing", "Routing", "{54C42F57-5447-4C21-9812-4AF665567566}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Routing", "src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj", "{352D08E0-782E-4AE3-99B2-7BC5B2C3F227}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Routing", "src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj", "{352D08E0-782E-4AE3-99B2-7BC5B2C3F227}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Routing.Abstractions", "Routing.Abstractions", "{3ACCF669-89AB-4B02-809E-E94C81271BFF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Routing.Abstractions", "src\Http\Routing.Abstractions\src\Microsoft.AspNetCore.Routing.Abstractions.csproj", "{DEED8B59-B811-471F-936A-D8D71AA97CE1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Routing.Abstractions", "src\Http\Routing.Abstractions\src\Microsoft.AspNetCore.Routing.Abstractions.csproj", "{DEED8B59-B811-471F-936A-D8D71AA97CE1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SignalR.Common", "SignalR.Common", "{DF5E3AC1-94B3-4B8A-B534-9D30924A8E6B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Common", "src\SignalR\common\SignalR.Common\src\Microsoft.AspNetCore.SignalR.Common.csproj", "{B2660D8F-8990-46C7-9B41-F3F94C6402F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Common", "src\SignalR\common\SignalR.Common\src\Microsoft.AspNetCore.SignalR.Common.csproj", "{B2660D8F-8990-46C7-9B41-F3F94C6402F6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{4F99BF26-E900-487C-828F-145166926DE2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Core", "src\SignalR\server\Core\src\Microsoft.AspNetCore.SignalR.Core.csproj", "{31E763AD-996E-44EF-A70E-1B8187922C98}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Core", "src\SignalR\server\Core\src\Microsoft.AspNetCore.SignalR.Core.csproj", "{31E763AD-996E-44EF-A70E-1B8187922C98}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Protocols.Json", "Protocols.Json", "{989F2C7A-807F-41DA-AA5F-08147E8FF433}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Protocols.Json", "src\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj", "{3974EB1C-077A-4A79-B295-87B62446C266}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.Json", "src\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj", "{3974EB1C-077A-4A79-B295-87B62446C266}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StaticFiles", "StaticFiles", "{9AA5D0E4-B341-4570-B3E4-081E6653471D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.StaticFiles", "src\Middleware\StaticFiles\src\Microsoft.AspNetCore.StaticFiles.csproj", "{CE49768E-1D4C-4724-9D35-1D0F73E49C9D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.StaticFiles", "src\Middleware\StaticFiles\src\Microsoft.AspNetCore.StaticFiles.csproj", "{CE49768E-1D4C-4724-9D35-1D0F73E49C9D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSockets", "WebSockets", "{B7ED4805-AD3E-4D76-BC5B-700A31488493}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.WebSockets", "src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj", "{AB3A72FB-5DF6-4F95-BAA8-19CF5BC97760}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets", "src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj", "{AB3A72FB-5DF6-4F95-BAA8-19CF5BC97760}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebUtilities", "WebUtilities", "{BF4B36D6-D1FA-41A3-9CB3-2E1D2894E2AD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.WebUtilities", "src\Http\WebUtilities\src\Microsoft.AspNetCore.WebUtilities.csproj", "{06492CD6-4C51-41F3-9D1A-0A95F914F871}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebUtilities", "src\Http\WebUtilities\src\Microsoft.AspNetCore.WebUtilities.csproj", "{06492CD6-4C51-41F3-9D1A-0A95F914F871}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headers", "Headers", "{C59FBB3C-3A7F-4744-A823-1EB92C5B23A2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Net.Http.Headers", "src\Http\Headers\src\Microsoft.Net.Http.Headers.csproj", "{62BE5BF4-24BB-4013-ACE8-3D76521FA77C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Net.Http.Headers", "src\Http\Headers\src\Microsoft.Net.Http.Headers.csproj", "{62BE5BF4-24BB-4013-ACE8-3D76521FA77C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{7321E95C-0A37-45BD-B458-1BA4412A14AC}" EndProject @@ -275,21 +275,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kestrel", "Kestrel", "{4FDD EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kestrel", "Kestrel", "{89472057-8BB2-44A8-B0FC-D9F3ADB1181C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Server.Kestrel", "src\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj", "{D40C86C9-0E5D-4974-84D8-A835B58B2A8F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel", "src\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj", "{D40C86C9-0E5D-4974-84D8-A835B58B2A8F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{D69B5668-86EF-4CC5-814B-113BAF740DD0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Server.Kestrel.Core", "src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj", "{0674F69B-FAC8-43E5-8711-A476936F459F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Core", "src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj", "{0674F69B-FAC8-43E5-8711-A476936F459F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HttpsPolicy", "HttpsPolicy", "{D9403AE4-77D1-44DE-8785-280C342D215A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.HttpsPolicy", "src\Middleware\HttpsPolicy\src\Microsoft.AspNetCore.HttpsPolicy.csproj", "{5DEF52C0-B440-4DAC-8A2D-920C7016C580}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpsPolicy", "src\Middleware\HttpsPolicy\src\Microsoft.AspNetCore.HttpsPolicy.csproj", "{5DEF52C0-B440-4DAC-8A2D-920C7016C580}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IIS", "IIS", "{D67E977E-75DF-41EE-8315-6DBF5C2B7357}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IISIntegration", "IISIntegration", "{EE74FCEC-542A-4B66-B0A9-49B28049C203}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Server.IISIntegration", "src\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{737D2BC0-092E-404B-BB73-EACAEDBF0E65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration", "src\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{737D2BC0-092E-404B-BB73-EACAEDBF0E65}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{7004082D-53E9-45C2-B2DE-EB3CE448B64F}" EndProject @@ -317,7 +317,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureAppServicesSample", "s EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosting", "Hosting", "{68C2D913-06D4-4EAC-9283-78465BF214E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Hosting", "src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj", "{D2CA01D0-954A-4F90-AF9B-F081DA9F40E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Hosting", "src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj", "{D2CA01D0-954A-4F90-AF9B-F081DA9F40E3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server.IntegrationTesting", "Server.IntegrationTesting", "{F4B3C10B-F713-45D1-84EF-DD503BA09D20}" EndProject @@ -353,97 +353,97 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Authen EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{A274799D-3D1F-4AE5-A154-4BF6C80A8D94}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Analyzers", "src\Components\Analyzers\src\Microsoft.AspNetCore.Components.Analyzers.csproj", "{D5DF181B-E759-4797-ABAF-0BFDEC2A883D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Analyzers", "src\Components\Analyzers\src\Microsoft.AspNetCore.Components.Analyzers.csproj", "{D5DF181B-E759-4797-ABAF-0BFDEC2A883D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Analyzers.Tests", "src\Components\Analyzers\test\Microsoft.AspNetCore.Components.Analyzers.Tests.csproj", "{7B382EC7-57B6-4C0C-8B27-5F63B5184193}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Analyzers.Tests", "src\Components\Analyzers\test\Microsoft.AspNetCore.Components.Analyzers.Tests.csproj", "{7B382EC7-57B6-4C0C-8B27-5F63B5184193}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebAssembly", "WebAssembly", "{562D5067-8CD8-4F19-BCBB-873204932C61}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebAssembly", "WebAssembly", "{58FFC6CE-5958-460C-A520-32CF3FD0BE61}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly", "src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj", "{DE6F167E-F41C-414D-923E-8E51E992A830}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly", "src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj", "{DE6F167E-F41C-414D-923E-8E51E992A830}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.Tests", "src\Components\WebAssembly\WebAssembly\test\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", "{839C479D-64DC-4A59-85E4-04E5A2CC9631}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Tests", "src\Components\WebAssembly\WebAssembly\test\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", "{839C479D-64DC-4A59-85E4-04E5A2CC9631}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevServer", "DevServer", "{26196065-4F78-435E-B500-2967A63D277D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.DevServer", "src\Components\WebAssembly\DevServer\src\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj", "{ADB4912C-BCD5-400F-BD20-37410BA76EF8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.DevServer", "src\Components\WebAssembly\DevServer\src\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj", "{ADB4912C-BCD5-400F-BD20-37410BA76EF8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{13683DEB-FB7E-4F20-ACB2-015381943541}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.Server", "src\Components\WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj", "{1721A1A3-D8A0-4CEE-A11D-4D168F93F0D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Server", "src\Components\WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj", "{1721A1A3-D8A0-4CEE-A11D-4D168F93F0D7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Performance", "src\Components\Components\perf\Microsoft.AspNetCore.Components.Performance.csproj", "{4510DA15-69F3-411E-86F2-7AD04FFB9823}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Performance", "src\Components\Components\perf\Microsoft.AspNetCore.Components.Performance.csproj", "{4510DA15-69F3-411E-86F2-7AD04FFB9823}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Tests", "src\Components\Components\test\Microsoft.AspNetCore.Components.Tests.csproj", "{B46A45E2-27E8-4C85-920B-1BFF45B7EBEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Tests", "src\Components\Components\test\Microsoft.AspNetCore.Components.Tests.csproj", "{B46A45E2-27E8-4C85-920B-1BFF45B7EBEB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Server.Tests", "src\Components\Server\test\Microsoft.AspNetCore.Components.Server.Tests.csproj", "{6CBF1AA9-1073-4A97-B867-FB534C6829C1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Server.Tests", "src\Components\Server\test\Microsoft.AspNetCore.Components.Server.Tests.csproj", "{6CBF1AA9-1073-4A97-B867-FB534C6829C1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0508E463-0269-40C9-B5C2-3B600FB2A28B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.E2ETests", "src\Components\test\E2ETest\Microsoft.AspNetCore.Components.E2ETests.csproj", "{A71B95EE-1BFF-49BA-BA5F-121EFAE41667}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.E2ETests", "src\Components\test\E2ETest\Microsoft.AspNetCore.Components.E2ETests.csproj", "{A71B95EE-1BFF-49BA-BA5F-121EFAE41667}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DefaultBuilder", "DefaultBuilder", "{881FE4C3-1553-4CA1-B430-DDD37B3493AB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore", "src\DefaultBuilder\src\Microsoft.AspNetCore.csproj", "{DAAB40E7-73DD-4BF1-BCB7-FE3E20F2F7C1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore", "src\DefaultBuilder\src\Microsoft.AspNetCore.csproj", "{DAAB40E7-73DD-4BF1-BCB7-FE3E20F2F7C1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics", "Diagnostics", "{DE5C0805-1C4A-4C70-B613-89EE2EA0B757}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics", "src\Middleware\Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj", "{FB86B2B2-60B2-477A-9D78-2530A0089660}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Diagnostics", "src\Middleware\Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj", "{FB86B2B2-60B2-477A-9D78-2530A0089660}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostFiltering", "HostFiltering", "{58915BB2-CEF5-4CA3-8886-A61156564505}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.HostFiltering", "src\Middleware\HostFiltering\src\Microsoft.AspNetCore.HostFiltering.csproj", "{7FC5CFC7-9BFE-4C19-90DE-A84A76A8E03D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HostFiltering", "src\Middleware\HostFiltering\src\Microsoft.AspNetCore.HostFiltering.csproj", "{7FC5CFC7-9BFE-4C19-90DE-A84A76A8E03D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HttpOverrides", "HttpOverrides", "{39086512-EBC8-4061-BE34-DCCA5D1BA585}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.HttpOverrides", "src\Middleware\HttpOverrides\src\Microsoft.AspNetCore.HttpOverrides.csproj", "{34F24889-22D2-40A1-A2AB-A43B9061FE0D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpOverrides", "src\Middleware\HttpOverrides\src\Microsoft.AspNetCore.HttpOverrides.csproj", "{34F24889-22D2-40A1-A2AB-A43B9061FE0D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NodeServices", "NodeServices", "{ED90A0D9-867B-4212-846F-3E09D60A5B7E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.NodeServices", "src\Middleware\NodeServices\src\Microsoft.AspNetCore.NodeServices.csproj", "{C68A3531-E47A-4F2F-842E-4A3A7C844CC1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices", "src\Middleware\NodeServices\src\Microsoft.AspNetCore.NodeServices.csproj", "{C68A3531-E47A-4F2F-842E-4A3A7C844CC1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResponseCompression", "ResponseCompression", "{512EFCA7-1590-492A-8D06-84744F79DA91}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.ResponseCompression", "src\Middleware\ResponseCompression\src\Microsoft.AspNetCore.ResponseCompression.csproj", "{CC783D3A-71CB-4DFD-9769-9EC7EF1ADF1B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ResponseCompression", "src\Middleware\ResponseCompression\src\Microsoft.AspNetCore.ResponseCompression.csproj", "{CC783D3A-71CB-4DFD-9769-9EC7EF1ADF1B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SpaServices.Extensions", "SpaServices.Extensions", "{B06D06BD-DE60-46E8-AC05-0C1D39E40638}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SpaServices.Extensions", "src\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{566B6729-63FF-484D-8F47-91561D76F445}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "src\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{566B6729-63FF-484D-8F47-91561D76F445}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SpaServices", "SpaServices", "{1EBEF6FF-4A0D-4668-A9F3-74587ECAC969}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SpaServices", "src\Middleware\SpaServices\src\Microsoft.AspNetCore.SpaServices.csproj", "{797B9228-5BC9-4C0C-B444-C490A98D057E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices", "src\Middleware\SpaServices\src\Microsoft.AspNetCore.SpaServices.csproj", "{797B9228-5BC9-4C0C-B444-C490A98D057E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Analyzers", "Mvc.Analyzers", "{515282B6-6EF9-46E0-8EF1-DBD1CD948D9E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Analyzers", "src\Mvc\Mvc.Analyzers\src\Microsoft.AspNetCore.Mvc.Analyzers.csproj", "{02A85F31-A092-4322-A3D9-91E894D9ECD2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers", "src\Mvc\Mvc.Analyzers\src\Microsoft.AspNetCore.Mvc.Analyzers.csproj", "{02A85F31-A092-4322-A3D9-91E894D9ECD2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IIS", "IIS", "{33CAD745-5912-47D3-BAF3-5AE580FED275}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Server.IIS", "src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj", "{9E01AF6A-F748-4490-B45B-8558D1E701B4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IIS", "src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj", "{9E01AF6A-F748-4490-B45B-8558D1E701B4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Transport.Sockets", "Transport.Sockets", "{94174359-D57E-467E-8AC7-167A10260F34}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets", "src\Servers\Kestrel\Transport.Sockets\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj", "{94F7E7AE-4AEF-44EC-B9F2-ABD1A5AC9E4A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets", "src\Servers\Kestrel\Transport.Sockets\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj", "{94F7E7AE-4AEF-44EC-B9F2-ABD1A5AC9E4A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Protocols.MessagePack", "Protocols.MessagePack", "{B4B39C18-70F5-4C07-9871-4E124F63F1F7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Protocols.MessagePack", "src\SignalR\common\Protocols.MessagePack\src\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj", "{B2D225EF-BE70-4FA0-B631-0A4D24DB7BD7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.MessagePack", "src\SignalR\common\Protocols.MessagePack\src\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj", "{B2D225EF-BE70-4FA0-B631-0A4D24DB7BD7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Protocols.NewtonsoftJson", "Protocols.NewtonsoftJson", "{01EED30B-3DC5-4591-A4C2-B17C6D36B1DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson", "src\SignalR\common\Protocols.NewtonsoftJson\src\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj", "{EDFF668F-1DB2-4EC7-BB6F-9DDBF70437CB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson", "src\SignalR\common\Protocols.NewtonsoftJson\src\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj", "{EDFF668F-1DB2-4EC7-BB6F-9DDBF70437CB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.NewtonsoftJson", "Mvc.NewtonsoftJson", "{4B6BA2CC-8DE7-4070-A912-D51375998553}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.NewtonsoftJson", "src\Mvc\Mvc.NewtonsoftJson\src\Microsoft.AspNetCore.Mvc.NewtonsoftJson.csproj", "{18FCF9CD-8545-41FC-9E4B-0F18B593863D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.NewtonsoftJson", "src\Mvc\Mvc.NewtonsoftJson\src\Microsoft.AspNetCore.Mvc.NewtonsoftJson.csproj", "{18FCF9CD-8545-41FC-9E4B-0F18B593863D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Features", "Features", "{F94AF7F5-7DB1-4716-B4B5-A7DAB066AF2F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JsonPatch", "JsonPatch", "{5FF36127-063D-4D3E-9429-3BFD32713FEE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.JsonPatch", "src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj", "{5BFA4A13-895A-4680-BCC6-3BDA8F048A29}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.JsonPatch", "src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj", "{5BFA4A13-895A-4680-BCC6-3BDA8F048A29}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "clients", "clients", "{0338EC37-E5AD-47DA-9502-24A5F7433ADE}" EndProject @@ -451,73 +451,73 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "csharp", "csharp", "{2637A2 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{A6D39EEA-CC08-4ECF-BD2E-134D845876FB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Client", "src\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj", "{95F132DA-D9B6-4181-8D1B-A58D935B1827}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Client", "src\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj", "{95F132DA-D9B6-4181-8D1B-A58D935B1827}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client.Core", "Client.Core", "{85EFA9E2-A041-438B-BAF9-928C74571141}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Client.Core", "src\SignalR\clients\csharp\Client.Core\src\Microsoft.AspNetCore.SignalR.Client.Core.csproj", "{9443F095-DA20-4EED-BC97-5BF92470D972}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Client.Core", "src\SignalR\clients\csharp\Client.Core\src\Microsoft.AspNetCore.SignalR.Client.Core.csproj", "{9443F095-DA20-4EED-BC97-5BF92470D972}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http.Connections.Client", "Http.Connections.Client", "{EA798F59-BCF8-4013-AF6B-9611FDC9919C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Connections.Client", "src\SignalR\clients\csharp\Http.Connections.Client\src\Microsoft.AspNetCore.Http.Connections.Client.csproj", "{C10C8A03-9ECC-4CDC-9D8E-9E0FDF12E4EE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Connections.Client", "src\SignalR\clients\csharp\Http.Connections.Client\src\Microsoft.AspNetCore.Http.Connections.Client.csproj", "{C10C8A03-9ECC-4CDC-9D8E-9E0FDF12E4EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Web.Tests", "src\Components\Web\test\Microsoft.AspNetCore.Components.Web.Tests.csproj", "{9EA5219C-3C11-45D4-BD43-8BE05FB2812A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Tests", "src\Components\Web\test\Microsoft.AspNetCore.Components.Web.Tests.csproj", "{9EA5219C-3C11-45D4-BD43-8BE05FB2812A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authorization", "Authorization", "{9DF052F3-D239-4BB5-B41E-DA93A086B4F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Authorization", "src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj", "{CBA2A020-E44D-4AFF-ADB8-E6414E7EAD94}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Authorization", "src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj", "{CBA2A020-E44D-4AFF-ADB8-E6414E7EAD94}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Authorization.Tests", "src\Components\Authorization\test\Microsoft.AspNetCore.Components.Authorization.Tests.csproj", "{418848BA-83AE-49E4-9EEC-7AFB5591D8E8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Authorization.Tests", "src\Components\Authorization\test\Microsoft.AspNetCore.Components.Authorization.Tests.csproj", "{418848BA-83AE-49E4-9EEC-7AFB5591D8E8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Forms", "Forms", "{59ECEDFE-72E7-495D-BECB-784B0018544E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Forms", "src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj", "{61F6B56C-0764-4585-9C3F-63A0CF05E56E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Forms", "src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj", "{61F6B56C-0764-4585-9C3F-63A0CF05E56E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.Forms.Tests", "src\Components\Forms\test\Microsoft.AspNetCore.Components.Forms.Tests.csproj", "{60B11F05-CA3B-4AD1-BAC1-912C19D37395}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Forms.Tests", "src\Components\Forms\test\Microsoft.AspNetCore.Components.Forms.Tests.csproj", "{60B11F05-CA3B-4AD1-BAC1-912C19D37395}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{5FE1FBC1-8CE3-4355-9866-44FE1307C5F1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerApp", "src\Components\Samples\BlazorServerApp\BlazorServerApp.csproj", "{8DE6625B-C9E4-4949-A75C-89E3FF556724}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServerApp", "src\Components\Samples\BlazorServerApp\BlazorServerApp.csproj", "{8DE6625B-C9E4-4949-A75C-89E3FF556724}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ignitor", "Ignitor", "{9E6FF2AC-6424-49A5-8189-623FFB2A9B4C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ignitor", "src\Components\Ignitor\src\Ignitor.csproj", "{BE5D6903-34B9-4C29-85A2-811A7EA06DAF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ignitor", "src\Components\Ignitor\src\Ignitor.csproj", "{BE5D6903-34B9-4C29-85A2-811A7EA06DAF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ignitor.Test", "src\Components\Ignitor\test\Ignitor.Test.csproj", "{F3F89B56-66A9-4EBC-8658-80785827237E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ignitor.Test", "src\Components\Ignitor\test\Ignitor.Test.csproj", "{F3F89B56-66A9-4EBC-8658-80785827237E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarkapps", "benchmarkapps", "{0CE1CC26-98CE-4022-A81C-E32AAFC9B819}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wasm.Performance", "Wasm.Performance", "{6276A9A0-791B-49C1-AD8F-50AC47CDC196}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasm.Performance.Driver", "src\Components\benchmarkapps\Wasm.Performance\Driver\Wasm.Performance.Driver.csproj", "{B81C7FA1-870F-4F21-A928-A5BE18754E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Performance.Driver", "src\Components\benchmarkapps\Wasm.Performance\Driver\Wasm.Performance.Driver.csproj", "{B81C7FA1-870F-4F21-A928-A5BE18754E6E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasm.Performance.TestApp", "src\Components\benchmarkapps\Wasm.Performance\TestApp\Wasm.Performance.TestApp.csproj", "{2AEACF69-7F68-414A-B49D-2C627D37D0F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Performance.TestApp", "src\Components\benchmarkapps\Wasm.Performance\TestApp\Wasm.Performance.TestApp.csproj", "{2AEACF69-7F68-414A-B49D-2C627D37D0F6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.Server.Tests", "src\Components\WebAssembly\Server\test\Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj", "{0C610220-E00C-4752-98A0-44A3D4B96A21}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Server.Tests", "src\Components\WebAssembly\Server\test\Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj", "{0C610220-E00C-4752-98A0-44A3D4B96A21}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authentication.Msal", "Authentication.Msal", "{1FD5F261-6384-4AE1-A6DA-4D08A0BCE1CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Authentication.WebAssembly.Msal", "src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj", "{09F72EF0-2BDE-4B73-B116-A87E38C432FE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Authentication.WebAssembly.Msal", "src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj", "{09F72EF0-2BDE-4B73-B116-A87E38C432FE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DebugProxy", "DebugProxy", "{F01E5B0D-1277-481D-8879-41A87F3F9524}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.DebugProxy", "src\Components\WebAssembly\DebugProxy\src\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", "{67F51062-6897-4019-AA88-6BDB5E30B015}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.DebugProxy", "src\Components\WebAssembly\DebugProxy\src\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", "{67F51062-6897-4019-AA88-6BDB5E30B015}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JSInterop", "JSInterop", "{44161B20-CC30-403A-AC94-247592ED7590}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop.WebAssembly", "src\Components\WebAssembly\JSInterop\src\Microsoft.JSInterop.WebAssembly.csproj", "{E0B1F2AA-4EBA-4DC7-92D5-2F081354C8DE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.WebAssembly", "src\Components\WebAssembly\JSInterop\src\Microsoft.JSInterop.WebAssembly.csproj", "{E0B1F2AA-4EBA-4DC7-92D5-2F081354C8DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebAssembly.Authentication", "WebAssembly.Authentication", "{E33C36A1-481C-4A93-BCBE-22CCBA53349B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.Authentication", "src\Components\WebAssembly\WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj", "{E167A806-EEFA-4BCF-A14D-D985BAEA9387}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Authentication", "src\Components\WebAssembly\WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj", "{E167A806-EEFA-4BCF-A14D-D985BAEA9387}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests", "src\Components\WebAssembly\WebAssembly.Authentication\test\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "{15A90CE7-886D-4005-8C14-CF29123344E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests", "src\Components\WebAssembly\WebAssembly.Authentication\test\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "{15A90CE7-886D-4005-8C14-CF29123344E1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JSInterop", "JSInterop", "{E50C2015-42A4-4E71-94B9-62773D369FEE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.JSInterop", "Microsoft.JSInterop", "{16898702-3E33-41C1-B8D8-4CE3F1D46BD9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop", "src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj", "{70B719CD-C70E-4417-B1EE-FD24B5AFB0B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj", "{70B719CD-C70E-4417-B1EE-FD24B5AFB0B7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Tests", "src\DataProtection\Abstractions\test\Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj", "{552EB148-0518-41A6-905D-4696A6438E80}" EndProject @@ -1421,6 +1421,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerComparison.Functional EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Testing.Tests", "src\Testing\test\Microsoft.AspNetCore.Testing.Tests.csproj", "{1542DC58-1836-4191-A9C5-51D1716D2543}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web.Extensions", "Web.Extensions", "{F71FE795-9923-461B-9809-BB1821A276D0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions", "src\Components\Web.Extensions\src\Microsoft.AspNetCore.Components.Web.Extensions.csproj", "{8294A74F-7DAA-4B69-BC56-7634D93C9693}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions.Tests", "src\Components\Web.Extensions\test\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "{157605CB-5170-4C1A-980F-4BAE42DB60DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1430,9 +1436,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {03C2290A-1C48-489A-81DB-F3447B0DA595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {03C2290A-1C48-489A-81DB-F3447B0DA595}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -6702,6 +6705,33 @@ Global {1542DC58-1836-4191-A9C5-51D1716D2543}.Release|x64.Build.0 = Release|Any CPU {1542DC58-1836-4191-A9C5-51D1716D2543}.Release|x86.ActiveCfg = Release|Any CPU {1542DC58-1836-4191-A9C5-51D1716D2543}.Release|x86.Build.0 = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|x64.ActiveCfg = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|x64.Build.0 = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|x86.ActiveCfg = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Debug|x86.Build.0 = Debug|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|Any CPU.Build.0 = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|x64.ActiveCfg = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|x64.Build.0 = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|x86.ActiveCfg = Release|Any CPU + {8294A74F-7DAA-4B69-BC56-7634D93C9693}.Release|x86.Build.0 = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|x64.Build.0 = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Debug|x86.Build.0 = Debug|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|Any CPU.Build.0 = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.ActiveCfg = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.Build.0 = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.ActiveCfg = Release|Any CPU + {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {0F84F170-57D0-496B-8E2C-7984178EF69F} = {C28A32F6-8314-412E-9F3B-CBD31C23E878} @@ -7411,5 +7441,11 @@ Global {3CBC4802-E9B8-48B7-BC8C-B0AFB9EEC643} = {0ACCEDA7-339C-4B4D-8DD4-1AC271F31C04} {48E64014-B249-4644-8AEB-CDEE8ABA0DC2} = {3CBC4802-E9B8-48B7-BC8C-B0AFB9EEC643} {1542DC58-1836-4191-A9C5-51D1716D2543} = {05A169C7-4F20-4516-B10A-B13C5649D346} + {F71FE795-9923-461B-9809-BB1821A276D0} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} + {8294A74F-7DAA-4B69-BC56-7634D93C9693} = {F71FE795-9923-461B-9809-BB1821A276D0} + {157605CB-5170-4C1A-980F-4BAE42DB60DE} = {F71FE795-9923-461B-9809-BB1821A276D0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} EndGlobalSection EndGlobal diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 91343d8b13..4cca372972 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -63,6 +63,7 @@ + diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index aec5a0973c..c202f7c1c6 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -100,6 +100,8 @@ "src\\Components\\test\\testassets\\ComponentsApp.Server\\ComponentsApp.Server.csproj", "src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj", "src\\Components\\benchmarkapps\\Wasm.Performance\\TestApp\\Wasm.Performance.TestApp.csproj", + "src\\Components\\Web.Extensions\\src\\Microsoft.AspNetCore.Components.Web.Extensions.csproj", + "src\\Components\\Web.Extensions\\test\\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "src\\Components\\WebAssembly\\Server\\test\\Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj", "src\\Components\\WebAssembly\\Authentication.Msal\\src\\Microsoft.Authentication.WebAssembly.Msal.csproj", "src\\Components\\WebAssembly\\DebugProxy\\src\\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", diff --git a/src/Components/Components/src/Properties/AssemblyInfo.cs b/src/Components/Components/src/Properties/AssemblyInfo.cs index d5aa38e01d..45c4bb49ab 100644 --- a/src/Components/Components/src/Properties/AssemblyInfo.cs +++ b/src/Components/Components/src/Properties/AssemblyInfo.cs @@ -7,3 +7,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Web.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Web.Extensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 4f72337402..6dbf031fd3 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -16,6 +16,8 @@ "src\\Components\\Samples\\BlazorServerApp\\BlazorServerApp.csproj", "src\\Components\\Server\\src\\Microsoft.AspNetCore.Components.Server.csproj", "src\\Components\\Server\\test\\Microsoft.AspNetCore.Components.Server.Tests.csproj", + "src\\Components\\Web.Extensions\\src\\Microsoft.AspNetCore.Components.Web.Extensions.csproj", + "src\\Components\\Web.Extensions\\test\\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "src\\Components\\WebAssembly\\Authentication.Msal\\src\\Microsoft.Authentication.WebAssembly.Msal.csproj", "src\\Components\\WebAssembly\\DebugProxy\\src\\Microsoft.AspNetCore.Components.WebAssembly.DebugProxy.csproj", "src\\Components\\WebAssembly\\DevServer\\src\\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj", diff --git a/src/Components/Web.Extensions/src/Microsoft.AspNetCore.Components.Web.Extensions.csproj b/src/Components/Web.Extensions/src/Microsoft.AspNetCore.Components.Web.Extensions.csproj new file mode 100644 index 0000000000..10bf2f9234 --- /dev/null +++ b/src/Components/Web.Extensions/src/Microsoft.AspNetCore.Components.Web.Extensions.csproj @@ -0,0 +1,20 @@ + + + + $(DefaultNetCoreTargetFramework) + A collection of Blazor components for the web. + true + Microsoft.AspNetCore.Components + enable + + + + + + + + + + + + diff --git a/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorage.cs b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorage.cs new file mode 100644 index 0000000000..89ed322562 --- /dev/null +++ b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorage.cs @@ -0,0 +1,170 @@ +// 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.Concurrent; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.JSInterop; + +namespace Microsoft.AspNetCore.Components.Web.Extensions +{ + /// + /// Provides mechanisms for storing and retrieving data in the browser storage. + /// + public abstract class ProtectedBrowserStorage + { + private readonly string _storeName; + private readonly IJSRuntime _jsRuntime; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ConcurrentDictionary _cachedDataProtectorsByPurpose + = new ConcurrentDictionary(StringComparer.Ordinal); + + /// + /// Constructs an instance of . + /// + /// The name of the store in which the data should be stored. + /// The . + /// The . + protected ProtectedBrowserStorage(string storeName, IJSRuntime jsRuntime, IDataProtectionProvider dataProtectionProvider) + { + // Performing data protection on the client would give users a false sense of security, so we'll prevent this. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Browser)) + { + throw new PlatformNotSupportedException($"{GetType()} cannot be used when running in a browser."); + } + + if (string.IsNullOrEmpty(storeName)) + { + throw new ArgumentException("The value cannot be null or empty", nameof(storeName)); + } + + _storeName = storeName; + _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); + _dataProtectionProvider = dataProtectionProvider ?? throw new ArgumentNullException(nameof(dataProtectionProvider)); + } + + /// + /// + /// Asynchronously stores the specified data. + /// + /// + /// Since no data protection purpose is specified with this overload, the purpose is derived from + /// and the store name. This is a good default purpose to use if the keys come from a + /// fixed set known at compile-time. + /// + /// + /// A value specifying the name of the storage slot to use. + /// A JSON-serializable value to be stored. + /// A representing the completion of the operation. + public ValueTask SetAsync(string key, object value) + => SetAsync(CreatePurposeFromKey(key), key, value); + + /// + /// Asynchronously stores the supplied data. + /// + /// + /// A string that defines a scope for the data protection. The protected data can only + /// be unprotected by code that specifies the same purpose. + /// + /// A value specifying the name of the storage slot to use. + /// A JSON-serializable value to be stored. + /// A representing the completion of the operation. + public ValueTask SetAsync(string purpose, string key, object value) + { + if (string.IsNullOrEmpty(purpose)) + { + throw new ArgumentException("Cannot be null or empty", nameof(purpose)); + } + + if (string.IsNullOrEmpty(key)) + { + throw new ArgumentException("Cannot be null or empty", nameof(key)); + } + + return SetProtectedJsonAsync(key, Protect(purpose, value)); + } + + /// + /// + /// Asynchronously retrieves the specified data. + /// + /// + /// Since no data protection purpose is specified with this overload, the purpose is derived from + /// and the store name. This is a good default purpose to use if the keys come from a + /// fixed set known at compile-time. + /// + /// + /// A value specifying the name of the storage slot to use. + /// A representing the completion of the operation. + public ValueTask> GetAsync(string key) + => GetAsync(CreatePurposeFromKey(key), key); + + /// + /// + /// Asynchronously retrieves the specified data. + /// + /// + /// + /// A string that defines a scope for the data protection. The protected data can only + /// be unprotected if the same purpose was previously specified when calling + /// . + /// + /// A value specifying the name of the storage slot to use. + /// A representing the completion of the operation. + public async ValueTask> GetAsync(string purpose, string key) + { + var protectedJson = await GetProtectedJsonAsync(key); + + return protectedJson == null ? + new ProtectedBrowserStorageResult(false, default) : + new ProtectedBrowserStorageResult(true, Unprotect(purpose, protectedJson)); + } + + /// + /// Asynchronously deletes any data stored for the specified key. + /// + /// + /// A value specifying the name of the storage slot whose value should be deleted. + /// + /// A representing the completion of the operation. + public ValueTask DeleteAsync(string key) + => _jsRuntime.InvokeVoidAsync($"{_storeName}.removeItem", key); + + private string Protect(string purpose, object value) + { + var json = JsonSerializer.Serialize(value, options: JsonSerializerOptionsProvider.Options); + var protector = GetOrCreateCachedProtector(purpose); + + return protector.Protect(json); + } + + private TValue Unprotect(string purpose, string protectedJson) + { + var protector = GetOrCreateCachedProtector(purpose); + var json = protector.Unprotect(protectedJson); + + return JsonSerializer.Deserialize(json, options: JsonSerializerOptionsProvider.Options)!; + } + + private ValueTask SetProtectedJsonAsync(string key, string protectedJson) + => _jsRuntime.InvokeVoidAsync($"{_storeName}.setItem", key, protectedJson); + + private ValueTask GetProtectedJsonAsync(string key) + => _jsRuntime.InvokeAsync($"{_storeName}.getItem", key); + + // IDataProtect isn't disposable, so we're fine holding these indefinitely. + // Only a bounded number of them will be created, as the 'key' values should + // come from a bounded set known at compile-time. There's no use case for + // letting runtime data determine the 'key' values. + private IDataProtector GetOrCreateCachedProtector(string purpose) + => _cachedDataProtectorsByPurpose.GetOrAdd( + purpose, + _dataProtectionProvider.CreateProtector); + + private string CreatePurposeFromKey(string key) + => $"{GetType().FullName}:{_storeName}:{key}"; + } +} diff --git a/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageResult.cs b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageResult.cs new file mode 100644 index 0000000000..a666be1dc5 --- /dev/null +++ b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageResult.cs @@ -0,0 +1,28 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.AspNetCore.Components.Web.Extensions +{ + /// + /// Contains the result of a protected browser storage operation. + /// + public readonly struct ProtectedBrowserStorageResult + { + /// + /// Gets whether the operation succeeded. + /// + public bool Success { get; } + + /// + /// Gets the result value of the operation. + /// + [MaybeNull] + [AllowNull] + public T Value { get; } + + internal ProtectedBrowserStorageResult(bool success, [AllowNull] T value) + { + Success = success; + Value = value; + } + } +} diff --git a/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageServiceCollectionExtensions.cs b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageServiceCollectionExtensions.cs new file mode 100644 index 0000000000..1e06de723e --- /dev/null +++ b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedBrowserStorageServiceCollectionExtensions.cs @@ -0,0 +1,24 @@ +// 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.Components.Web.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for registering Protected Browser Storage services. + /// + public static class ProtectedBrowserStorageServiceCollectionExtensions + { + /// + /// Adds services for protected browser storage to the specified . + /// + /// The . + public static void AddProtectedBrowserStorage(this IServiceCollection services) + { + services.AddDataProtection(); + services.AddScoped(); + services.AddScoped(); + } + } +} diff --git a/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedLocalStorage.cs b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedLocalStorage.cs new file mode 100644 index 0000000000..68229e0f18 --- /dev/null +++ b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedLocalStorage.cs @@ -0,0 +1,30 @@ +// 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.JSInterop; + +namespace Microsoft.AspNetCore.Components.Web.Extensions +{ + /// + /// Provides mechanisms for storing and retrieving data in the browser's + /// 'localStorage' collection. + /// + /// This data will be scoped to the current user's browser, shared across + /// all tabs. The data will persist across browser restarts. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage + /// + public class ProtectedLocalStorage : ProtectedBrowserStorage + { + /// + /// Constructs an instance of . + /// + /// The . + /// The . + public ProtectedLocalStorage(IJSRuntime jsRuntime, IDataProtectionProvider dataProtectionProvider) + : base("localStorage", jsRuntime, dataProtectionProvider) + { + } + } +} diff --git a/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedSessionStorage.cs b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedSessionStorage.cs new file mode 100644 index 0000000000..452e4e2bb5 --- /dev/null +++ b/src/Components/Web.Extensions/src/ProtectedBrowserStorage/ProtectedSessionStorage.cs @@ -0,0 +1,30 @@ +// 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.JSInterop; + +namespace Microsoft.AspNetCore.Components.Web.Extensions +{ + /// + /// Provides mechanisms for storing and retrieving data in the browser's + /// 'sessionStorage' collection. + /// + /// This data will be scoped to the current browser tab. The data will be + /// discarded if the user closes the browser tab or closes the browser itself. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage + /// + public class ProtectedSessionStorage : ProtectedBrowserStorage + { + /// + /// Constructs an instance of . + /// + /// The . + /// The . + public ProtectedSessionStorage(IJSRuntime jsRuntime, IDataProtectionProvider dataProtectionProvider) + : base("sessionStorage", jsRuntime, dataProtectionProvider) + { + } + } +} diff --git a/src/Components/Web.Extensions/test/Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj b/src/Components/Web.Extensions/test/Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj new file mode 100644 index 0000000000..f09b7de9ec --- /dev/null +++ b/src/Components/Web.Extensions/test/Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj @@ -0,0 +1,19 @@ + + + + $(DefaultNetCoreTargetFramework) + Microsoft.AspNetCore.Components + + + + + + + + + + + + + + diff --git a/src/Components/Web.Extensions/test/ProtectedBrowserStorageTest.cs b/src/Components/Web.Extensions/test/ProtectedBrowserStorageTest.cs new file mode 100644 index 0000000000..8143890de0 --- /dev/null +++ b/src/Components/Web.Extensions/test/ProtectedBrowserStorageTest.cs @@ -0,0 +1,386 @@ +// 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.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.JSInterop; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Web.Extensions +{ + public class ProtectedBrowserStorageTest + { + [Fact] + public void SetAsync_ProtectsAndInvokesJS_DefaultPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var jsResultTask = new ValueTask((object)null); + var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; + var keyName = "testKey"; + var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:{keyName}"; + + // Act + jsRuntime.NextInvocationResult = jsResultTask; + var result = protectedBrowserStorage.SetAsync(keyName, data); + + // Assert + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.setItem", invocation.Identifier); + Assert.Collection(invocation.Args, + arg => Assert.Equal(keyName, arg), + arg => Assert.Equal( + "{\"stringProperty\":\"Hello\",\"intProperty\":123}", + TestDataProtectionProvider.Unprotect(expectedPurpose, (string)arg))); + } + + [Fact] + public void SetAsync_ProtectsAndInvokesJS_CustomPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var jsResultTask = new ValueTask((object)null); + var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; + var keyName = "testKey"; + var customPurpose = "my custom purpose"; + + // Act + jsRuntime.NextInvocationResult = jsResultTask; + var result = protectedBrowserStorage.SetAsync(customPurpose, keyName, data); + + // Assert + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.setItem", invocation.Identifier); + Assert.Collection(invocation.Args, + arg => Assert.Equal(keyName, arg), + arg => Assert.Equal( + "{\"stringProperty\":\"Hello\",\"intProperty\":123}", + TestDataProtectionProvider.Unprotect(customPurpose, (string)arg))); + } + + + [Fact] + public void SetAsync_ProtectsAndInvokesJS_NullValue() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var jsResultTask = new ValueTask((object)null); + var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:testKey"; + + // Act + jsRuntime.NextInvocationResult = jsResultTask; + var result = protectedBrowserStorage.SetAsync("testKey", null); + + // Assert + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.setItem", invocation.Identifier); + Assert.Collection(invocation.Args, + arg => Assert.Equal("testKey", arg), + arg => Assert.Equal( + "null", + TestDataProtectionProvider.Unprotect(expectedPurpose, (string)arg))); + } + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_ValidData_DefaultPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; + var keyName = "testKey"; + var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:{keyName}"; + var storedJson = "{\"StringProperty\":\"Hello\",\"IntProperty\":123}"; + jsRuntime.NextInvocationResult = new ValueTask( + TestDataProtectionProvider.Protect(expectedPurpose, storedJson)); + + // Act + var result = await protectedBrowserStorage.GetAsync(keyName); + + // Assert + Assert.True(result.Success); + Assert.Equal("Hello", result.Value.StringProperty); + Assert.Equal(123, result.Value.IntProperty); + + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.getItem", invocation.Identifier); + Assert.Collection(invocation.Args, arg => Assert.Equal(keyName, arg)); + } + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_ValidData_CustomPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; + var keyName = "testKey"; + var customPurpose = "my custom purpose"; + var storedJson = "{\"StringProperty\":\"Hello\",\"IntProperty\":123}"; + jsRuntime.NextInvocationResult = new ValueTask( + TestDataProtectionProvider.Protect(customPurpose, storedJson)); + + // Act + var result = await protectedBrowserStorage.GetAsync(customPurpose, keyName); + + // Assert + Assert.True(result.Success); + Assert.Equal("Hello", result.Value.StringProperty); + Assert.Equal(123, result.Value.IntProperty); + + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.getItem", invocation.Identifier); + Assert.Collection(invocation.Args, arg => Assert.Equal(keyName, arg)); + } + + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_NoValue() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + jsRuntime.NextInvocationResult = new ValueTask((string)null); + + // Act + var result = await protectedBrowserStorage.GetAsync("testKey"); + + // Assert + Assert.False(result.Success); + Assert.Null(result.Value); + } + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_InvalidJson() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:testKey"; + var storedJson = "you can't parse this"; + jsRuntime.NextInvocationResult = new ValueTask( + TestDataProtectionProvider.Protect(expectedPurpose, storedJson)); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + async () => await protectedBrowserStorage.GetAsync("testKey")); + } + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_InvalidProtection_Plaintext() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var storedString = "This string is not even protected"; + + jsRuntime.NextInvocationResult = new ValueTask(storedString); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + async () => await protectedBrowserStorage.GetAsync("testKey")); + } + + [Fact] + public async Task GetAsync_InvokesJSAndUnprotects_InvalidProtection_Base64Encoded() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + + // DataProtection deals with strings by base64-encoding the results. + // Depending on whether the stored data is base64-encoded or not, + // it will trigger a different failure point in data protection. + var storedString = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes("This string is not even protected")); + + jsRuntime.NextInvocationResult = new ValueTask(storedString); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + async () => await protectedBrowserStorage.GetAsync("testKey")); + } + + + [Fact] + public async Task GetValueOrDefaultAsync_InvokesJSAndUnprotects_WrongPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:testKey"; + var storedJson = "we won't even try to parse this"; + jsRuntime.NextInvocationResult = new ValueTask( + TestDataProtectionProvider.Protect(expectedPurpose, storedJson)); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + async () => await protectedBrowserStorage.GetAsync("different key")); + var innerException = ex.InnerException; + Assert.IsType(innerException); + Assert.Contains("The value is not protected with the expected purpose", innerException.Message); + } + + [Fact] + public void DeleteAsync_InvokesJS() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + var nextTask = new ValueTask((object)null); + jsRuntime.NextInvocationResult = nextTask; + + // Act + var result = protectedBrowserStorage.DeleteAsync("testKey"); + + // Assert + var invocation = jsRuntime.Invocations.Single(); + Assert.Equal("testStore.removeItem", invocation.Identifier); + Assert.Collection(invocation.Args, arg => Assert.Equal("testKey", arg)); + } + + [Fact] + public async Task ReusesCachedProtectorsByPurpose() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + jsRuntime.NextInvocationResult = new ValueTask((object)null); + var dataProtectionProvider = new TestDataProtectionProvider(); + var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); + + // Act + await protectedBrowserStorage.SetAsync("key 1", null); + await protectedBrowserStorage.SetAsync("key 2", null); + await protectedBrowserStorage.SetAsync("key 1", null); + await protectedBrowserStorage.SetAsync("key 3", null); + + // Assert + var typeName = typeof(TestProtectedBrowserStorage).FullName; + var expectedPurposes = new[] + { + $"{typeName}:testStore:key 1", + $"{typeName}:testStore:key 2", + $"{typeName}:testStore:key 3" + }; + Assert.Equal(expectedPurposes, dataProtectionProvider.ProtectorsCreated.ToArray()); + + Assert.Collection(jsRuntime.Invocations, + invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]), + invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[1], "null"), invocation.Args[1]), + invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]), + invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[2], "null"), invocation.Args[1])); + } + + class TestModel + { + public string StringProperty { get; set; } + + public int IntProperty { get; set; } + } + + class TestDataProtectionProvider : IDataProtectionProvider + { + public List ProtectorsCreated { get; } = new List(); + + + public static string Protect(string purpose, string plaintext) + => new TestDataProtector(purpose).Protect(plaintext); + + public static string Unprotect(string purpose, string protectedValue) + => new TestDataProtector(purpose).Unprotect(protectedValue); + + public IDataProtector CreateProtector(string purpose) + { + ProtectorsCreated.Add(purpose); + return new TestDataProtector(purpose); + } + + class TestDataProtector : IDataProtector + { + private readonly string _purpose; + + public TestDataProtector(string purpose) + { + _purpose = purpose; + } + + public IDataProtector CreateProtector(string purpose) + { + throw new NotImplementedException(); + } + + public byte[] Protect(byte[] plaintext) + { + // The test cases will only involve passing data that was originally converted from strings + var plaintextString = Encoding.UTF8.GetString(plaintext); + var fakeProtectedString = $"{ProtectionPrefix(_purpose)}{plaintextString}"; + return Encoding.UTF8.GetBytes(fakeProtectedString); + } + + public byte[] Unprotect(byte[] protectedData) + { + // The test cases will only involve passing data that was originally converted from strings + var protectedString = Encoding.UTF8.GetString(protectedData); + + var expectedPrefix = ProtectionPrefix(_purpose); + if (!protectedString.StartsWith(expectedPrefix, StringComparison.Ordinal)) + { + throw new ArgumentException($"The value is not protected with the expected purpose '{_purpose}'. Value supplied: '{protectedString}'", nameof(protectedData)); + } + + var unprotectedString = protectedString.Substring(expectedPrefix.Length); + return Encoding.UTF8.GetBytes(unprotectedString); + } + + private static string ProtectionPrefix(string purpose) + => $"PROTECTED:{purpose}:"; + } + } + + class TestJSRuntime : IJSRuntime + { + public List<(string Identifier, object[] Args)> Invocations { get; } + = new List<(string Identifier, object[] Args)>(); + + public object NextInvocationResult { get; set; } + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args) + { + Invocations.Add((identifier, args)); + return (ValueTask)NextInvocationResult; + } + + public ValueTask InvokeAsync(string identifier, object[] args) + => InvokeAsync(identifier, cancellationToken: CancellationToken.None, args: args); + } + + class TestProtectedBrowserStorage : ProtectedBrowserStorage + { + public TestProtectedBrowserStorage(string storeName, IJSRuntime jsRuntime, IDataProtectionProvider dataProtectionProvider) + : base(storeName, jsRuntime, dataProtectionProvider) + { + } + } + } +} diff --git a/src/Components/test/E2ETest/ServerExecutionTests/ProtectedBrowserStorageUsageTest.cs b/src/Components/test/E2ETest/ServerExecutionTests/ProtectedBrowserStorageUsageTest.cs new file mode 100644 index 0000000000..5908dffc4b --- /dev/null +++ b/src/Components/test/E2ETest/ServerExecutionTests/ProtectedBrowserStorageUsageTest.cs @@ -0,0 +1,144 @@ +// 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.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; +using TestServer; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests +{ + public class ProtectedBrowserStorageUsageTest : ServerTestBase> + { + public ProtectedBrowserStorageUsageTest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + public override async Task InitializeAsync() + { + // Since browser storage needs to be reset in between tests, it's easiest for each + // test to run in its own browser instance. + await base.InitializeAsync(Guid.NewGuid().ToString()); + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase); + Browser.MountTestComponent(); + } + + [Fact] + public void LocalStoragePersistsOnRefresh() + { + // Local storage initially cleared + var incrementLocalButton = Browser.FindElement(By.Id("increment-local")); + var localCount = Browser.FindElement(By.Id("local-count")); + Browser.Equal("0", () => localCount.Text); + + // Local storage updates + incrementLocalButton.Click(); + Browser.Equal("1", () => localCount.Text); + + // Local storage persists on refresh + Browser.Navigate().Refresh(); + Browser.MountTestComponent(); + + localCount = Browser.FindElement(By.Id("local-count")); + Browser.Equal("1", () => localCount.Text); + } + + [Fact] + public void LocalStoragePersistsAcrossTabs() + { + // Local storage initially cleared + var incrementLocalButton = Browser.FindElement(By.Id("increment-local")); + var localCount = Browser.FindElement(By.Id("local-count")); + Browser.Equal("0", () => localCount.Text); + + // Local storage updates in current tab + incrementLocalButton.Click(); + Browser.Equal("1", () => localCount.Text); + + // Local storage persists across tabs + OpenNewSession(); + localCount = Browser.FindElement(By.Id("local-count")); + Browser.Equal("1", () => localCount.Text); + } + + [Fact] + public void SessionStoragePersistsOnRefresh() + { + // Session storage initially cleared + var incrementSessionButton = Browser.FindElement(By.Id("increment-session")); + var sessionCount = Browser.FindElement(By.Id("session-count")); + Browser.Equal("0", () => sessionCount.Text); + + // Session storage updates + incrementSessionButton.Click(); + Browser.Equal("1", () => sessionCount.Text); + + // Session storage persists on refresh + Browser.Navigate().Refresh(); + Browser.MountTestComponent(); + + sessionCount = Browser.FindElement(By.Id("session-count")); + Browser.Equal("1", () => sessionCount.Text); + } + + [Fact] + public void SessionStorageDoesNotPersistAcrossTabs() + { + // Session storage initially cleared + var incrementSessionButton = Browser.FindElement(By.Id("increment-session")); + var sessionCount = Browser.FindElement(By.Id("session-count")); + Browser.Equal("0", () => sessionCount.Text); + + // Session storage updates in current tab + incrementSessionButton.Click(); + Browser.Equal("1", () => sessionCount.Text); + + // Session storage does not persist across tabs + OpenNewSession(); + sessionCount = Browser.FindElement(By.Id("session-count")); + Browser.Equal("0", () => sessionCount.Text); + } + + /// + /// Opens a new session in a new tab, mounting a new test component. + /// + /// + /// Simply opening a new tab using JS is not sufficient because the browser context perists and + /// the same session is maintained. The way this method starts a new session is by simulating a + /// ctrl+click (or command+click on Mac) on a link that opens a new tab. This opens it as a background + /// tab, which has a new browser context. + /// + private void OpenNewSession() + { + var modifierKey = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? + Keys.Command : + Keys.Control; + + var newTabLink = Browser.FindElement(By.Id("new-tab")); + var action = new Actions(Browser); + action.KeyDown(modifierKey).MoveToElement(newTabLink).Click().KeyUp(modifierKey).Perform(); + + Browser.SwitchTo().Window(Browser.WindowHandles.Last()); + + Navigate(ServerPathBase); + Browser.MountTestComponent(); + } + } +} diff --git a/src/Components/test/E2ETest/Tests/ProtectedBrowserStorageInjectionTest.cs b/src/Components/test/E2ETest/Tests/ProtectedBrowserStorageInjectionTest.cs new file mode 100644 index 0000000000..d3ae79c944 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/ProtectedBrowserStorageInjectionTest.cs @@ -0,0 +1,71 @@ +// 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 BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests +{ + public class ProtectedBrowserStorageInjectionTest : ServerTestBase> + { + public ProtectedBrowserStorageInjectionTest( + BrowserFixture browserFixture, + ToggleExecutionModeServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client); + Browser.MountTestComponent(); + } + + [Fact] + public void ThrowsWhenInjectingProtectedLocalStorageIfAndOnlyIfWebAssembly() + { + var messageElement = Browser.FindElement(By.Id("message")); + var injectLocalButton = Browser.FindElement(By.Id("inject-local")); + + Browser.Equal("Waiting for injection...", () => messageElement.Text); + + injectLocalButton.Click(); + + if (_serverFixture.ExecutionMode == ExecutionMode.Client) + { + Browser.Contains("cannot be used when running in a browser.", () => messageElement.Text); + } + else + { + Browser.Equal("Success!", () => messageElement.Text); + } + } + + [Fact] + public void ThrowsWhenInjectingProtectedSessionStorageIfAndOnlyIfWebAssembly() + { + var messageElement = Browser.FindElement(By.Id("message")); + var injectSessionButton = Browser.FindElement(By.Id("inject-session")); + + Browser.Equal("Waiting for injection...", () => messageElement.Text); + + injectSessionButton.Click(); + + if (_serverFixture.ExecutionMode == ExecutionMode.Client) + { + Browser.Contains("cannot be used when running in a browser.", () => messageElement.Text); + } + else + { + Browser.Equal("Success!", () => messageElement.Text); + } + } + } +} diff --git a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj index d3455b0778..bbcc855357 100644 --- a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj +++ b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj @@ -17,6 +17,7 @@ + diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index 41562e3843..aa87700d5b 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -63,6 +63,8 @@ + + diff --git a/src/Components/test/testassets/BasicTestApp/Program.cs b/src/Components/test/testassets/BasicTestApp/Program.cs index b512810bc1..1e640b59dc 100644 --- a/src/Components/test/testassets/BasicTestApp/Program.cs +++ b/src/Components/test/testassets/BasicTestApp/Program.cs @@ -10,6 +10,7 @@ using System.Web; using BasicTestApp.AuthTest; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Web.Extensions; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Http; using Microsoft.AspNetCore.Components.WebAssembly.Services; @@ -38,12 +39,16 @@ namespace BasicTestApp policy.RequireAssertion(ctx => ctx.User.Identity.Name?.StartsWith("B") ?? false)); }); + builder.Services.AddDataProtection(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging")); builder.Logging.Services.AddSingleton(s => new PrependMessageLoggerProvider(builder.Configuration["Logging:PrependMessage:Message"], s.GetService())); - var host = builder.Build(); ConfigureCulture(host); diff --git a/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageInjectionComponent.razor b/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageInjectionComponent.razor new file mode 100644 index 0000000000..db55ea6bc2 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageInjectionComponent.razor @@ -0,0 +1,29 @@ +@using Microsoft.Extensions.DependencyInjection +@using Microsoft.AspNetCore.Components.Web.Extensions +@inject IServiceProvider ServiceProvider + + + + +

@message

+ +@code { + private string message = "Waiting for injection..."; + + private void Inject() where T : ProtectedBrowserStorage + { + try + { + var localStorage = ServiceProvider.GetService(); + message = "Success!"; + } + catch (PlatformNotSupportedException ex) + { + message = ex.Message; + } + catch + { + message = "Unexpected exception encountered."; + } + } +} diff --git a/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageUsageComponent.razor b/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageUsageComponent.razor new file mode 100644 index 0000000000..8a1f70fa08 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/ProtectedBrowserStorageUsageComponent.razor @@ -0,0 +1,56 @@ +@using Microsoft.AspNetCore.Components.Web.Extensions +@inject ProtectedLocalStorage ProtectedLocalStore +@inject ProtectedSessionStorage ProtectedSessionStore + +New tab (needed for tests) + +
+ @if (localCount.HasValue) + { +

Local count: @localCount

+ + } +
+ +
+ @if (sessionCount.HasValue) + { +

Session count: @sessionCount

+ + } +
+ +@code { + int? localCount; + int? sessionCount; + + protected override async Task OnInitializedAsync() + { + await LoadLocalCount(); + await LoadSessionCount(); + } + + private async Task LoadLocalCount() + { + var localResult = await ProtectedLocalStore.GetAsync("localCount"); + localCount = localResult.Success ? localResult.Value : 0; + } + + private async Task LoadSessionCount() + { + var sessionResult = await ProtectedSessionStore.GetAsync("sessionCount"); + sessionCount = sessionResult.Success ? sessionResult.Value : 0; + } + + private async Task IncrementLocalCount() + { + await ProtectedLocalStore.SetAsync("localCount", localCount + 1); + await LoadLocalCount(); + } + + private async Task IncrementSessionCount() + { + await ProtectedSessionStore.SetAsync("sessionCount", sessionCount + 1); + await LoadSessionCount(); + } +} diff --git a/src/Components/test/testassets/TestServer/Components.TestServer.csproj b/src/Components/test/testassets/TestServer/Components.TestServer.csproj index 8cc342db87..651b301bec 100644 --- a/src/Components/test/testassets/TestServer/Components.TestServer.csproj +++ b/src/Components/test/testassets/TestServer/Components.TestServer.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Components/test/testassets/TestServer/ServerStartup.cs b/src/Components/test/testassets/TestServer/ServerStartup.cs index bcd6e56e88..90d6e862fa 100644 --- a/src/Components/test/testassets/TestServer/ServerStartup.cs +++ b/src/Components/test/testassets/TestServer/ServerStartup.cs @@ -20,6 +20,7 @@ namespace TestServer { services.AddMvc(); services.AddServerSideBlazor(); + services.AddProtectedBrowserStorage(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.