diff --git a/.github/workflows/runtime-sync.yml b/.github/workflows/runtime-sync.yml index b6236ab7b468..03e7e8f9d6ba 100644 --- a/.github/workflows/runtime-sync.yml +++ b/.github/workflows/runtime-sync.yml @@ -21,12 +21,14 @@ jobs: # Test this script using changes in a fork repository: 'dotnet/aspnetcore' path: aspnetcore + ref: release/5.0 - name: Checkout runtime uses: actions/checkout@v2.0.0 with: # Test this script using changes in a fork repository: 'dotnet/runtime' path: runtime + ref: release/5.0 - name: Copy shell: cmd working-directory: .\runtime\src\libraries\Common\src\System\Net\Http\aspnetcore\ @@ -65,5 +67,6 @@ jobs: title: 'Sync shared code from runtime' body: 'This PR was automatically generated to sync shared code changes from runtime. Fixes #18943' labels: area-servers + base: release/5.0 branch: github-action/sync-runtime branch-suffix: timestamp diff --git a/AspNetCore.sln b/AspNetCore.sln index f0f0fe403186..16238f73a34b 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1423,13 +1423,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{FED4267E-E5E4-49C5-98DB-8B3F203596EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly", "src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", "{6B2734BF-C61D-4889-ABBF-456A4075D59B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.BlazorWebAssembly", "src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", "{6B2734BF-C61D-4889-ABBF-456A4075D59B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tests", "src\Components\WebAssembly\Sdk\test\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", "{83371889-9A3E-4D16-AE77-EB4F83BC6374}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tests", "src\Components\WebAssembly\Sdk\test\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", "{83371889-9A3E-4D16-AE77-EB4F83BC6374}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests", "src\Components\WebAssembly\Sdk\integrationtests\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "{525EBCB4-A870-470B-BC90-845306C337D1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests", "src\Components\WebAssembly\Sdk\integrationtests\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "{525EBCB4-A870-470B-BC90-845306C337D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tools", "src\Components\WebAssembly\Sdk\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", "{175E5CD8-92D4-46BB-882E-3A930D3302D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tools", "src\Components\WebAssembly\Sdk\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", "{175E5CD8-92D4-46BB-882E-3A930D3302D4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{6126DCE4-9692-4EE2-B240-C65743572995}" EndProject @@ -1455,7 +1455,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteropClient", "src\Grpc\t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteropWebsite", "src\Grpc\test\testassets\InteropWebsite\InteropWebsite.csproj", "{19189670-E206-471D-94F8-7D3D545E5020}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasm.Performance.ConsoleHost", "src\Components\benchmarkapps\Wasm.Performance\ConsoleHost\Wasm.Performance.ConsoleHost.csproj", "{E9408723-E6A9-4715-B906-3B25B0238ABA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Performance.ConsoleHost", "src\Components\benchmarkapps\Wasm.Performance\ConsoleHost\Wasm.Performance.ConsoleHost.csproj", "{E9408723-E6A9-4715-B906-3B25B0238ABA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "src\Servers\IIS\IIS\test\testassets\InProcessWebSite\InProcessWebSite.csproj", "{8DA61885-B95E-4BA1-A752-C79B6597FC44}" EndProject @@ -1483,6 +1483,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Watch. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Watch.BrowserRefresh.Tests", "src\Tools\dotnet-watch\BrowserRefresh\test\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj", "{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{7D2B0799-A634-42AC-AE77-5D167BA51389}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Client", "src\Components\WebAssembly\testassets\HostedInAspNet.Client\HostedInAspNet.Client.csproj", "{9788C76F-658B-4441-88F8-22C6B86FAD27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "src\Components\WebAssembly\testassets\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{1970D5CD-D9A4-4673-A297-179BB04199F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "src\Components\WebAssembly\testassets\StandaloneApp\StandaloneApp.csproj", "{A40350FE-4334-4007-B1C3-6BEB1B070309}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Authentication.Client", "src\Components\WebAssembly\testassets\Wasm.Authentication.Client\Wasm.Authentication.Client.csproj", "{C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Authentication.Server", "src\Components\WebAssembly\testassets\Wasm.Authentication.Server\Wasm.Authentication.Server.csproj", "{FE5290C7-45DA-46F8-BD74-698E7A161DD6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Authentication.Shared", "src\Components\WebAssembly\testassets\Wasm.Authentication.Shared\Wasm.Authentication.Shared.csproj", "{ED66DC0E-FD6A-477A-BA8A-5273AA64F580}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -6809,30 +6823,6 @@ Global {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.Build.0 = Release|Any CPU {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.ActiveCfg = Release|Any CPU {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.Build.0 = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.ActiveCfg = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.Build.0 = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.ActiveCfg = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.Build.0 = Debug|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.Build.0 = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.ActiveCfg = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.Build.0 = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.ActiveCfg = Release|Any CPU - {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.Build.0 = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.ActiveCfg = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.Build.0 = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.ActiveCfg = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.Build.0 = Debug|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.Build.0 = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.ActiveCfg = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.Build.0 = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.ActiveCfg = Release|Any CPU - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.Build.0 = Release|Any CPU {46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|Any CPU.Build.0 = Debug|Any CPU {46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -7071,6 +7061,102 @@ Global {7E268085-1046-4362-80CB-2977FF826DCA}.Release|x64.Build.0 = Release|Any CPU {7E268085-1046-4362-80CB-2977FF826DCA}.Release|x86.ActiveCfg = Release|Any CPU {7E268085-1046-4362-80CB-2977FF826DCA}.Release|x86.Build.0 = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.Build.0 = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.Build.0 = Debug|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.Build.0 = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.ActiveCfg = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.Build.0 = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.ActiveCfg = Release|Any CPU + {A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.Build.0 = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.Build.0 = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.Build.0 = Debug|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.Build.0 = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.ActiveCfg = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.Build.0 = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.ActiveCfg = Release|Any CPU + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.Build.0 = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|x64.ActiveCfg = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|x64.Build.0 = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|x86.ActiveCfg = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Debug|x86.Build.0 = Debug|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|Any CPU.Build.0 = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|x64.ActiveCfg = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|x64.Build.0 = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|x86.ActiveCfg = Release|Any CPU + {9788C76F-658B-4441-88F8-22C6B86FAD27}.Release|x86.Build.0 = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|x64.ActiveCfg = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|x64.Build.0 = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Debug|x86.Build.0 = Debug|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|Any CPU.Build.0 = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|x64.ActiveCfg = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|x64.Build.0 = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|x86.ActiveCfg = Release|Any CPU + {1970D5CD-D9A4-4673-A297-179BB04199F4}.Release|x86.Build.0 = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|x64.ActiveCfg = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|x64.Build.0 = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|x86.ActiveCfg = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Debug|x86.Build.0 = Debug|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|Any CPU.Build.0 = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|x64.ActiveCfg = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|x64.Build.0 = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|x86.ActiveCfg = Release|Any CPU + {A40350FE-4334-4007-B1C3-6BEB1B070309}.Release|x86.Build.0 = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|x64.ActiveCfg = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|x64.Build.0 = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|x86.ActiveCfg = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Debug|x86.Build.0 = Debug|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|Any CPU.Build.0 = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|x64.ActiveCfg = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|x64.Build.0 = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|x86.ActiveCfg = Release|Any CPU + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F}.Release|x86.Build.0 = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|x64.Build.0 = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Debug|x86.Build.0 = Debug|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|x64.ActiveCfg = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|x64.Build.0 = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|x86.ActiveCfg = Release|Any CPU + {FE5290C7-45DA-46F8-BD74-698E7A161DD6}.Release|x86.Build.0 = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|x64.ActiveCfg = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|x64.Build.0 = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|x86.ActiveCfg = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Debug|x86.Build.0 = Debug|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|Any CPU.Build.0 = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|x64.ActiveCfg = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|x64.Build.0 = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|x86.ActiveCfg = Release|Any CPU + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -7788,8 +7874,6 @@ Global {83371889-9A3E-4D16-AE77-EB4F83BC6374} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} {525EBCB4-A870-470B-BC90-845306C337D1} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} {175E5CD8-92D4-46BB-882E-3A930D3302D4} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} - {A5CE25E9-89E1-4F2C-9B89-0C161707E700} = {B6118E15-C37A-4B05-B4DF-97FE99790417} - {E6A23627-8D63-4DF1-A4F2-8881172C1FE6} = {B6118E15-C37A-4B05-B4DF-97FE99790417} {6126DCE4-9692-4EE2-B240-C65743572995} = {0508E463-0269-40C9-B5C2-3B600FB2A28B} {46FB7E93-1294-4068-B80A-D4864F78277A} = {6126DCE4-9692-4EE2-B240-C65743572995} {25FA84DB-EEA7-4068-8E2D-F3D48B281C16} = {6126DCE4-9692-4EE2-B240-C65743572995} @@ -7814,6 +7898,15 @@ Global {7F87406C-A3C8-4139-A68D-E4C344294A67} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81} {1533E271-F61B-441B-8B74-59FB61DF0552} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81} {7E268085-1046-4362-80CB-2977FF826DCA} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81} + {A5CE25E9-89E1-4F2C-9B89-0C161707E700} = {B6118E15-C37A-4B05-B4DF-97FE99790417} + {E6A23627-8D63-4DF1-A4F2-8881172C1FE6} = {B6118E15-C37A-4B05-B4DF-97FE99790417} + {7D2B0799-A634-42AC-AE77-5D167BA51389} = {562D5067-8CD8-4F19-BCBB-873204932C61} + {9788C76F-658B-4441-88F8-22C6B86FAD27} = {7D2B0799-A634-42AC-AE77-5D167BA51389} + {1970D5CD-D9A4-4673-A297-179BB04199F4} = {7D2B0799-A634-42AC-AE77-5D167BA51389} + {A40350FE-4334-4007-B1C3-6BEB1B070309} = {7D2B0799-A634-42AC-AE77-5D167BA51389} + {C26965A9-EAC6-4E5A-B8C1-D161260EFE4F} = {7D2B0799-A634-42AC-AE77-5D167BA51389} + {FE5290C7-45DA-46F8-BD74-698E7A161DD6} = {7D2B0799-A634-42AC-AE77-5D167BA51389} + {ED66DC0E-FD6A-477A-BA8A-5273AA64F580} = {7D2B0799-A634-42AC-AE77-5D167BA51389} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 35853a5024d3..8da95c63148c 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -107,6 +107,7 @@ and are generated based on the last package release. + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ccaeca714720..27007a2ffa8c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -41,6 +41,10 @@ https://github.com/dotnet/efcore ede96af3fdf94ae6b149f10451d573c37fe27669 + + https://github.com/dotnet/efcore + 5099d918192f5df031e1ff5e3beea9cb361c605a + https://github.com/dotnet/runtime 907f7da59b40c80941b02ac2a46650adf3f606bc diff --git a/eng/Versions.props b/eng/Versions.props index e19722253c94..c79638b97759 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,6 +140,7 @@ 6.0.0-alpha.1.20417.8 6.0.0-alpha.1.20417.8 6.0.0-alpha.1.20417.8 + 6.0.0-alpha.1.20417.8 + + + + diff --git a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props index 6d0949542f9f..40fda88dac4a 100644 --- a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props +++ b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props @@ -20,6 +20,7 @@ $(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\build\netstandard2.0\Sdk.Razor.CurrentVersion.targets $(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.Razor\ $(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.BlazorWebAssembly\ + <_BlazorWebAssemblyTargetsFile>$(RepoRoot)src\Components\WebAssembly\Sdk\src\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets $(MSBuildThisFileDirectory)blazor.webassembly.js diff --git a/src/Components/WebAssembly/Server/src/ComponentsWebAssemblyApplicationBuilderExtensions.cs b/src/Components/WebAssembly/Server/src/ComponentsWebAssemblyApplicationBuilderExtensions.cs index 534dd3502dcc..9802b912dbbb 100644 --- a/src/Components/WebAssembly/Server/src/ComponentsWebAssemblyApplicationBuilderExtensions.cs +++ b/src/Components/WebAssembly/Server/src/ComponentsWebAssemblyApplicationBuilderExtensions.cs @@ -77,6 +77,7 @@ private static StaticFileOptions CreateStaticFilesOptions(IFileProvider webRootF AddMapping(contentTypeProvider, ".pdb", MediaTypeNames.Application.Octet); AddMapping(contentTypeProvider, ".br", MediaTypeNames.Application.Octet); AddMapping(contentTypeProvider, ".dat", MediaTypeNames.Application.Octet); + AddMapping(contentTypeProvider, ".blat", MediaTypeNames.Application.Octet); options.ContentTypeProvider = contentTypeProvider; diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/SatelliteResourcesLoader.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/SatelliteResourcesLoader.cs index 79f780c58496..9e7430308d19 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/SatelliteResourcesLoader.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/SatelliteResourcesLoader.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.WebAssembly.Services; @@ -55,7 +57,8 @@ public virtual async ValueTask LoadCurrentCultureResourcesAsync() for (var i = 0; i < assemblies.Length; i++) { - Assembly.Load((byte[])assemblies[i]); + using var stream = new MemoryStream((byte[])assemblies[i]); + AssemblyLoadContext.Default.LoadFromStream(stream); } } diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.Designer.cs b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.Designer.cs new file mode 100644 index 000000000000..3ef8ebfb2227 --- /dev/null +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.Designer.cs @@ -0,0 +1,405 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Wasm.Authentication.Server.Data; + +namespace Wasm.Authentication.Server.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20200818132003_IdentityServer4")] + partial class IdentityServer4 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20416.1"); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => + { + b.Property("UserCode") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("UserCode"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.ToTable("DeviceCodes"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Expiration") + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("PersistedGrants"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Wasm.Authentication.Server.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Wasm.Authentication.Server.Models.UserPreference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicationUserId") + .HasColumnType("TEXT"); + + b.Property("Color") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationUserId") + .IsUnique(); + + b.ToTable("UserPreferences"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Wasm.Authentication.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Wasm.Authentication.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Wasm.Authentication.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Wasm.Authentication.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Wasm.Authentication.Server.Models.UserPreference", b => + { + b.HasOne("Wasm.Authentication.Server.Models.ApplicationUser", null) + .WithOne("UserPreference") + .HasForeignKey("Wasm.Authentication.Server.Models.UserPreference", "ApplicationUserId"); + }); + + modelBuilder.Entity("Wasm.Authentication.Server.Models.ApplicationUser", b => + { + b.Navigation("UserPreference"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.cs b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.cs new file mode 100644 index 000000000000..59133a5862df --- /dev/null +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/20200818132003_IdentityServer4.cs @@ -0,0 +1,77 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Wasm.Authentication.Server.Data.Migrations +{ + public partial class IdentityServer4 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ConsumedTime", + table: "PersistedGrants", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "Description", + table: "PersistedGrants", + type: "TEXT", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "SessionId", + table: "PersistedGrants", + type: "TEXT", + maxLength: 100, + nullable: true); + + migrationBuilder.AddColumn( + name: "Description", + table: "DeviceCodes", + type: "TEXT", + maxLength: 200, + nullable: true); + + migrationBuilder.AddColumn( + name: "SessionId", + table: "DeviceCodes", + type: "TEXT", + maxLength: 100, + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants"); + + migrationBuilder.DropColumn( + name: "ConsumedTime", + table: "PersistedGrants"); + + migrationBuilder.DropColumn( + name: "Description", + table: "PersistedGrants"); + + migrationBuilder.DropColumn( + name: "SessionId", + table: "PersistedGrants"); + + migrationBuilder.DropColumn( + name: "Description", + table: "DeviceCodes"); + + migrationBuilder.DropColumn( + name: "SessionId", + table: "DeviceCodes"); + } + } +} diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 312d93c9a5da..c68c2e09660c 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -14,39 +14,47 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.2"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20416.1"); modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -61,33 +69,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -95,6 +114,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -108,18 +129,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -173,12 +194,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -215,12 +236,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); @@ -243,8 +264,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -256,12 +277,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -279,17 +300,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -371,6 +392,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .WithOne("UserPreference") .HasForeignKey("Wasm.Authentication.Server.Models.UserPreference", "ApplicationUserId"); }); + + modelBuilder.Entity("Wasm.Authentication.Server.Models.ApplicationUser", b => + { + b.Navigation("UserPreference"); + }); #pragma warning restore 612, 618 } } diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj index 8f0b70919811..142bfe2145f3 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Components/test/E2ETest/Tests/BindTest.cs b/src/Components/test/E2ETest/Tests/BindTest.cs index 1ee1b4ed974a..0e172331a9f0 100644 --- a/src/Components/test/E2ETest/Tests/BindTest.cs +++ b/src/Components/test/E2ETest/Tests/BindTest.cs @@ -979,8 +979,8 @@ public void CanBindTextboxNullableDateTime_InvalidValue() // Modify target to something invalid - the invalid change is reverted // back to the last valid value target.SendKeys(Keys.Control + "a"); // select all - target.SendKeys("05/06A"); - Browser.Equal("05/06A", () => target.GetAttribute("value")); + target.SendKeys("05/06X"); + Browser.Equal("05/06X", () => target.GetAttribute("value")); target.SendKeys("\t"); Browser.Equal(expected, () => DateTime.Parse(target.GetAttribute("value"))); Assert.Equal(expected, DateTime.Parse(boundValue.Text)); @@ -1017,8 +1017,8 @@ public void CanBindTextboxDateTimeOffset_InvalidValue() // Modify target to something invalid - the invalid change is reverted // back to the last valid value target.SendKeys(Keys.Control + "a"); // select all - target.SendKeys("05/06A"); - Browser.Equal("05/06A", () => target.GetAttribute("value")); + target.SendKeys("05/06X"); + Browser.Equal("05/06X", () => target.GetAttribute("value")); target.SendKeys("\t"); Browser.Equal(expected.DateTime, () => DateTimeOffset.Parse(target.GetAttribute("value")).DateTime); Assert.Equal(expected.DateTime, DateTimeOffset.Parse(boundValue.Text).DateTime); diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs index 998144b33cf7..52b25edefcb6 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs @@ -199,7 +199,7 @@ public void AuthenticatedUser_ProfileIncludesDetails_And_AccessToken() "profile", "Wasm.Authentication.ServerAPI" }, - payload.Scopes); + payload.Scopes.OrderBy(id => id)); var currentTime = DateTimeOffset.Parse(Browser.Exists(By.Id("current-time")).Text); var tokenExpiration = DateTimeOffset.Parse(Browser.Exists(By.Id("access-token-expires")).Text); diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs index 184c62e33c72..81c99ccf677a 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs @@ -22,7 +22,7 @@ public WebAssemblyLocalizationTest( { } - [Theory(Skip = "https://github.com/dotnet/runtime/issues/38124")] + [Theory] [InlineData("en-US", "Hello!")] [InlineData("fr-FR", "Bonjour!")] public void CanSetCultureAndReadLocalizedResources(string culture, string message) diff --git a/src/Components/test/testassets/BasicTestApp/VirtualizationComponent.razor b/src/Components/test/testassets/BasicTestApp/VirtualizationComponent.razor index 29aba9ea3e3b..69e776ccfdf7 100644 --- a/src/Components/test/testassets/BasicTestApp/VirtualizationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/VirtualizationComponent.razor @@ -22,7 +22,7 @@
Item @context
-
Loading item @context.Index...
+
Loading item @context.Index...
diff --git a/src/Grpc/test/InteropTests/InteropTests.cs b/src/Grpc/test/InteropTests/InteropTests.cs index cc02c4bee21c..839292ab4b52 100644 --- a/src/Grpc/test/InteropTests/InteropTests.cs +++ b/src/Grpc/test/InteropTests/InteropTests.cs @@ -25,7 +25,7 @@ public InteropTests(ITestOutputHelper output) _output = output; } - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task EmptyUnary() => InteropTestCase("empty_unary"); [Fact] @@ -36,20 +36,20 @@ public InteropTests(ITestOutputHelper output) [QuarantinedTest] public Task ClientStreaming() => InteropTestCase("client_streaming"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task ServerStreaming() => InteropTestCase("server_streaming"); [Fact] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/22101")] public Task PingPong() => InteropTestCase("ping_pong"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task EmptyStream() => InteropTestCase("empty_stream"); [Fact] public Task CancelAfterBegin() => InteropTestCase("cancel_after_begin"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task CancelAfterFirstResponse() => InteropTestCase("cancel_after_first_response"); [Fact] @@ -59,30 +59,31 @@ public InteropTests(ITestOutputHelper output) [QuarantinedTest] public Task CustomMetadata() => InteropTestCase("custom_metadata"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task StatusCodeAndMessage() => InteropTestCase("status_code_and_message"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task SpecialStatusMessage() => InteropTestCase("special_status_message"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task UnimplementedService() => InteropTestCase("unimplemented_service"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task UnimplementedMethod() => InteropTestCase("unimplemented_method"); [Fact] - [QuarantinedTest] + [QuarantinedTest("Server is getting 'identity' encoding. Will resolve in gRPC project when updated SDK is available.")] public Task ClientCompressedUnary() => InteropTestCase("client_compressed_unary"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] + [QuarantinedTest("Server is getting 'identity' encoding. Will resolve in gRPC project when updated SDK is available.")] public Task ClientCompressedStreaming() => InteropTestCase("client_compressed_streaming"); [Fact] [QuarantinedTest] public Task ServerCompressedUnary() => InteropTestCase("server_compressed_unary"); - [Fact(Skip= "https://github.com/dotnet/aspnetcore/issues/24902")] + [Fact] public Task ServerCompressedStreaming() => InteropTestCase("server_compressed_streaming"); private async Task InteropTestCase(string name) diff --git a/src/Grpc/test/testassets/InteropClient/InteropClient.cs b/src/Grpc/test/testassets/InteropClient/InteropClient.cs index 91ce76153820..6590d8ae6930 100644 --- a/src/Grpc/test/testassets/InteropClient/InteropClient.cs +++ b/src/Grpc/test/testassets/InteropClient/InteropClient.cs @@ -100,6 +100,7 @@ private InteropClient(ClientOptions options) { #pragma warning disable CS0618 // Type or member is obsolete loggerOptions.IncludeScopes = true; + loggerOptions.DisableColors = true; #pragma warning restore CS0618 // Type or member is obsolete }); }); @@ -167,7 +168,7 @@ private async Task HttpClientCreateChannel() httpClientHandler.ClientCertificates.Add(cert); } - var httpClient = new HttpClient(httpClientHandler); + var httpClient = new HttpClient(new VersionPolicyHandler(httpClientHandler)); var channel = GrpcChannel.ForAddress($"{scheme}://{options.ServerHost}:{options.ServerPort}", new GrpcChannelOptions { @@ -179,7 +180,20 @@ private async Task HttpClientCreateChannel() return new GrpcChannelWrapper(channel); } - private bool IsHttpClient() => string.Equals(options.ClientType, "httpclient", StringComparison.OrdinalIgnoreCase); + // TODO(JamesNK): This type can be removed in the future when Grpc.Net.Client sets VersionPolicy automatically. + // https://github.com/grpc/grpc-dotnet/pull/987 + private class VersionPolicyHandler : DelegatingHandler + { + public VersionPolicyHandler(HttpMessageHandler innerHandler) : base(innerHandler) + { + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + request.VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; + return base.SendAsync(request, cancellationToken); + } + } private async Task CreateCredentialsAsync(bool? useTestCaOverride = null) { diff --git a/src/Grpc/test/testassets/InteropClient/RunTests.ps1 b/src/Grpc/test/testassets/InteropClient/RunTests.ps1 index faa46d98c1b9..ae2f27046127 100644 --- a/src/Grpc/test/testassets/InteropClient/RunTests.ps1 +++ b/src/Grpc/test/testassets/InteropClient/RunTests.ps1 @@ -1,4 +1,4 @@ -Param +Param ( [bool]$use_tls = $false ) @@ -50,4 +50,4 @@ foreach ($test in $allTests) Write-Host } -Write-Host "Done" -ForegroundColor Cyan \ No newline at end of file +Write-Host "Done" -ForegroundColor Cyan diff --git a/src/Grpc/test/testassets/InteropWebsite/Program.cs b/src/Grpc/test/testassets/InteropWebsite/Program.cs index e160254f5d7f..c10a4256d461 100644 --- a/src/Grpc/test/testassets/InteropWebsite/Program.cs +++ b/src/Grpc/test/testassets/InteropWebsite/Program.cs @@ -37,7 +37,12 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureLogging(builder => { - builder.AddConsole(); + builder.AddConsole(loggerOptions => + { +#pragma warning disable CS0618 // Type or member is obsolete + loggerOptions.DisableColors = true; +#pragma warning restore CS0618 // Type or member is obsolete + }); builder.SetMinimumLevel(LogLevel.Trace); }) .ConfigureWebHostDefaults(webBuilder => diff --git a/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs b/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs index 918500cd2562..d4cb1f953bfe 100644 --- a/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs +++ b/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs @@ -135,7 +135,7 @@ private static void EnsureCompression(BoolValue? expectCompressed, ServerCallCon { // ServerCallContext.RequestHeaders filters out grpc-* headers // Get grpc-encoding from HttpContext instead - var encoding = context.GetHttpContext().Request.Headers.SingleOrDefault(h => h.Key == "grpc-encoding").Value.SingleOrDefault(); + var encoding = context.GetHttpContext().Request.Headers.SingleOrDefault(h => string.Equals(h.Key, "grpc-encoding", StringComparison.OrdinalIgnoreCase)).Value.SingleOrDefault(); if (expectCompressed.Value) { if (encoding == null || encoding == "identity") diff --git a/src/Http/Http/src/Features/ResponseCookiesFeature.cs b/src/Http/Http/src/Features/ResponseCookiesFeature.cs index 7e3cabb28e00..b51b99831ff6 100644 --- a/src/Http/Http/src/Features/ResponseCookiesFeature.cs +++ b/src/Http/Http/src/Features/ResponseCookiesFeature.cs @@ -15,7 +15,7 @@ public class ResponseCookiesFeature : IResponseCookiesFeature // Lambda hoisted to static readonly field to improve inlining https://github.com/dotnet/roslyn/issues/13624 private readonly static Func _nullResponseFeature = f => null; - private FeatureReferences _features; + private readonly IFeatureCollection _features; private IResponseCookies? _cookiesCollection; /// @@ -27,12 +27,7 @@ public class ResponseCookiesFeature : IResponseCookiesFeature /// public ResponseCookiesFeature(IFeatureCollection features) { - if (features == null) - { - throw new ArgumentNullException(nameof(features)); - } - - _features.Initalize(features); + _features = features ?? throw new ArgumentNullException(nameof(features)); } /// @@ -46,16 +41,9 @@ public ResponseCookiesFeature(IFeatureCollection features) [Obsolete("This constructor is obsolete and will be removed in a future version.")] public ResponseCookiesFeature(IFeatureCollection features, ObjectPool? builderPool) { - if (features == null) - { - throw new ArgumentNullException(nameof(features)); - } - - _features.Initalize(features); + _features = features ?? throw new ArgumentNullException(nameof(features)); } - private IHttpResponseFeature HttpResponseFeature => _features.Fetch(ref _features.Cache, _nullResponseFeature)!; - /// public IResponseCookies Cookies { @@ -63,8 +51,7 @@ public IResponseCookies Cookies { if (_cookiesCollection == null) { - var headers = HttpResponseFeature.Headers; - _cookiesCollection = new ResponseCookies(headers); + _cookiesCollection = new ResponseCookies(_features); } return _cookiesCollection; diff --git a/src/Http/Http/src/Internal/EventIds.cs b/src/Http/Http/src/Internal/EventIds.cs new file mode 100644 index 000000000000..ddefeceecf11 --- /dev/null +++ b/src/Http/Http/src/Internal/EventIds.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Http +{ + internal static class EventIds + { + public static readonly EventId SameSiteNotSecure = new EventId(1, "SameSiteNotSecure"); + } +} diff --git a/src/Http/Http/src/Internal/ResponseCookies.cs b/src/Http/Http/src/Internal/ResponseCookies.cs index d9adfb69f16b..e6e582bfda0b 100644 --- a/src/Http/Http/src/Internal/ResponseCookies.cs +++ b/src/Http/Http/src/Internal/ResponseCookies.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; @@ -16,18 +19,16 @@ internal class ResponseCookies : IResponseCookies internal const string EnableCookieNameEncoding = "Microsoft.AspNetCore.Http.EnableCookieNameEncoding"; internal bool _enableCookieNameEncoding = AppContext.TryGetSwitch(EnableCookieNameEncoding, out var enabled) && enabled; + private readonly IFeatureCollection _features; + private ILogger? _logger; + /// /// Create a new wrapper. /// - /// The for the response. - public ResponseCookies(IHeaderDictionary headers) + internal ResponseCookies(IFeatureCollection features) { - if (headers == null) - { - throw new ArgumentNullException(nameof(headers)); - } - - Headers = headers; + _features = features; + Headers = _features.Get().Headers; } private IHeaderDictionary Headers { get; set; } @@ -54,6 +55,21 @@ public void Append(string key, string value, CookieOptions options) throw new ArgumentNullException(nameof(options)); } + // SameSite=None cookies must be marked as Secure. + if (!options.Secure && options.SameSite == SameSiteMode.None) + { + if (_logger == null) + { + var services = _features.Get()?.RequestServices; + _logger = services?.GetService>(); + } + + if (_logger != null) + { + Log.SameSiteCookieNotSecure(_logger, key); + } + } + var setCookieHeaderValue = new SetCookieHeaderValue( _enableCookieNameEncoding ? Uri.EscapeDataString(key) : key, Uri.EscapeDataString(value)) @@ -135,5 +151,18 @@ public void Delete(string key, CookieOptions options) SameSite = options.SameSite }); } + + private static class Log + { + private static readonly Action _samesiteNotSecure = LoggerMessage.Define( + LogLevel.Warning, + EventIds.SameSiteNotSecure, + "The cookie '{name}' has set 'SameSite=None' and must also set 'Secure'."); + + public static void SameSiteCookieNotSecure(ILogger logger, string name) + { + _samesiteNotSecure(logger, name, null); + } + } } } diff --git a/src/Http/Http/test/ResponseCookiesTest.cs b/src/Http/Http/test/ResponseCookiesTest.cs index 8e4e60aaa9ab..2c73a75ec1f3 100644 --- a/src/Http/Http/test/ResponseCookiesTest.cs +++ b/src/Http/Http/test/ResponseCookiesTest.cs @@ -2,6 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; using Microsoft.Net.Http.Headers; using Xunit; @@ -9,11 +13,56 @@ namespace Microsoft.AspNetCore.Http.Tests { public class ResponseCookiesTest { + private IFeatureCollection MakeFeatures(IHeaderDictionary headers) + { + var responseFeature = new HttpResponseFeature() + { + Headers = headers + }; + var features = new FeatureCollection(); + features.Set(responseFeature); + return features; + } + + [Fact] + public void AppendSameSiteNoneWithoutSecureLogsWarning() + { + var headers = new HeaderDictionary(); + var features = MakeFeatures(headers); + var services = new ServiceCollection(); + + var sink = new TestSink(TestSink.EnableWithTypeName); + var loggerFactory = new TestLoggerFactory(sink, enabled: true); + services.AddLogging(); + services.AddSingleton(loggerFactory); + + features.Set(new ServiceProvidersFeature() { RequestServices = services.BuildServiceProvider() }); + + var cookies = new ResponseCookies(features); + var testCookie = "TestCookie"; + + cookies.Append(testCookie, "value", new CookieOptions() + { + SameSite = SameSiteMode.None, + }); + + var cookieHeaderValues = headers[HeaderNames.SetCookie]; + Assert.Single(cookieHeaderValues); + Assert.StartsWith(testCookie, cookieHeaderValues[0]); + Assert.Contains("path=/", cookieHeaderValues[0]); + Assert.Contains("samesite=none", cookieHeaderValues[0]); + Assert.DoesNotContain("secure", cookieHeaderValues[0]); + + var writeContext = Assert.Single(sink.Writes); + Assert.Equal("The cookie 'TestCookie' has set 'SameSite=None' and must also set 'Secure'.", writeContext.Message); + } + [Fact] public void DeleteCookieShouldSetDefaultPath() { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); var testCookie = "TestCookie"; cookies.Delete(testCookie); @@ -29,7 +78,8 @@ public void DeleteCookieShouldSetDefaultPath() public void DeleteCookieWithCookieOptionsShouldKeepPropertiesOfCookieOptions() { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); var testCookie = "TestCookie"; var time = new DateTimeOffset(2000, 1, 1, 1, 1, 1, 1, TimeSpan.Zero); var options = new CookieOptions @@ -58,7 +108,8 @@ public void DeleteCookieWithCookieOptionsShouldKeepPropertiesOfCookieOptions() public void NoParamsDeleteRemovesCookieCreatedByAdd() { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); var testCookie = "TestCookie"; cookies.Append(testCookie, testCookie); @@ -75,7 +126,8 @@ public void NoParamsDeleteRemovesCookieCreatedByAdd() public void ProvidesMaxAgeWithCookieOptionsArgumentExpectMaxAgeToBeSet() { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); var cookieOptions = new CookieOptions(); var maxAgeTime = TimeSpan.FromHours(1); cookieOptions.MaxAge = TimeSpan.FromHours(1); @@ -96,7 +148,8 @@ public void ProvidesMaxAgeWithCookieOptionsArgumentExpectMaxAgeToBeSet() public void EscapesValuesBeforeSettingCookie(string value, string expected) { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); cookies.Append("key", value); @@ -111,7 +164,8 @@ public void EscapesValuesBeforeSettingCookie(string value, string expected) public void InvalidKeysThrow(string key) { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); Assert.Throws(() => cookies.Append(key, "1")); } @@ -124,7 +178,8 @@ public void InvalidKeysThrow(string key) public void AppContextSwitchEscapesKeysAndValuesBeforeSettingCookie(string key, string value, string expected) { var headers = new HeaderDictionary(); - var cookies = new ResponseCookies(headers); + var features = MakeFeatures(headers); + var cookies = new ResponseCookies(features); cookies._enableCookieNameEncoding = true; cookies.Append(key, value); diff --git a/src/Http/Routing/perf/Matching/HttpMethodPolicyJumpTableBenchmark.cs b/src/Http/Routing/perf/Matching/HttpMethodPolicyJumpTableBenchmark.cs new file mode 100644 index 000000000000..012d138e9ae3 --- /dev/null +++ b/src/Http/Routing/perf/Matching/HttpMethodPolicyJumpTableBenchmark.cs @@ -0,0 +1,54 @@ +// 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.Collections.Generic; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Routing.Matching +{ + public class HttpMethodPolicyJumpTableBenchmark + { + private PolicyJumpTable _dictionaryJumptable; + private PolicyJumpTable _singleEntryJumptable; + private DefaultHttpContext _httpContext; + + [GlobalSetup] + public void Setup() + { + _dictionaryJumptable = new HttpMethodDictionaryPolicyJumpTable( + 0, + new Dictionary + { + [HttpMethods.Get] = 1 + }, + -1, + new Dictionary + { + [HttpMethods.Get] = 2 + }); + _singleEntryJumptable = new HttpMethodSingleEntryPolicyJumpTable( + 0, + HttpMethods.Get, + -1, + supportsCorsPreflight: true, + -1, + 2); + + _httpContext = new DefaultHttpContext(); + _httpContext.Request.Method = HttpMethods.Get; + } + + [Benchmark] + public int DictionaryPolicyJumpTable() + { + return _dictionaryJumptable.GetDestination(_httpContext); + } + + [Benchmark] + public int SingleEntryPolicyJumpTable() + { + return _singleEntryJumptable.GetDestination(_httpContext); + } + } +} diff --git a/src/Http/Routing/src/Matching/HttpMethodDictionaryPolicyJumpTable.cs b/src/Http/Routing/src/Matching/HttpMethodDictionaryPolicyJumpTable.cs new file mode 100644 index 000000000000..f77e65f37e2c --- /dev/null +++ b/src/Http/Routing/src/Matching/HttpMethodDictionaryPolicyJumpTable.cs @@ -0,0 +1,48 @@ +// 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.Collections.Generic; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Routing.Matching +{ + internal sealed class HttpMethodDictionaryPolicyJumpTable : PolicyJumpTable + { + private readonly int _exitDestination; + private readonly Dictionary? _destinations; + private readonly int _corsPreflightExitDestination; + private readonly Dictionary? _corsPreflightDestinations; + + private readonly bool _supportsCorsPreflight; + + public HttpMethodDictionaryPolicyJumpTable( + int exitDestination, + Dictionary? destinations, + int corsPreflightExitDestination, + Dictionary? corsPreflightDestinations) + { + _exitDestination = exitDestination; + _destinations = destinations; + _corsPreflightExitDestination = corsPreflightExitDestination; + _corsPreflightDestinations = corsPreflightDestinations; + + _supportsCorsPreflight = _corsPreflightDestinations != null && _corsPreflightDestinations.Count > 0; + } + + public override int GetDestination(HttpContext httpContext) + { + int destination; + + var httpMethod = httpContext.Request.Method; + if (_supportsCorsPreflight && HttpMethodMatcherPolicy.IsCorsPreflightRequest(httpContext, httpMethod, out var accessControlRequestMethod)) + { + return _corsPreflightDestinations!.TryGetValue(accessControlRequestMethod, out destination) + ? destination + : _corsPreflightExitDestination; + } + + return _destinations != null && + _destinations.TryGetValue(httpMethod, out destination) ? destination : _exitDestination; + } + } +} diff --git a/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs b/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs index 039d889fed53..9ffa13fa91fa 100644 --- a/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs +++ b/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs @@ -370,11 +370,38 @@ public PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList 0) + { + supportsCorsPreflight = true; + corsPreflightDestination = corsPreflightDestinations.Single().Value; + } + + return new HttpMethodSingleEntryPolicyJumpTable( + exitDestination, + method, + destination, + supportsCorsPreflight, + corsPreflightExitDestination, + corsPreflightDestination); + } + else + { + return new HttpMethodDictionaryPolicyJumpTable( + exitDestination, + destinations, + corsPreflightExitDestination, + corsPreflightDestinations); + } } private Endpoint CreateRejectionEndpoint(IEnumerable httpMethods) @@ -418,50 +445,15 @@ private static bool ContainsHttpMethod(List httpMethods, string httpMeth return false; } - private class HttpMethodPolicyJumpTable : PolicyJumpTable + internal static bool IsCorsPreflightRequest(HttpContext httpContext, string httpMethod, out StringValues accessControlRequestMethod) { - private readonly int _exitDestination; - private readonly Dictionary? _destinations; - private readonly int _corsPreflightExitDestination; - private readonly Dictionary? _corsPreflightDestinations; - - private readonly bool _supportsCorsPreflight; - - public HttpMethodPolicyJumpTable( - int exitDestination, - Dictionary? destinations, - int corsPreflightExitDestination, - Dictionary? corsPreflightDestinations) - { - _exitDestination = exitDestination; - _destinations = destinations; - _corsPreflightExitDestination = corsPreflightExitDestination; - _corsPreflightDestinations = corsPreflightDestinations; - - _supportsCorsPreflight = _corsPreflightDestinations != null && _corsPreflightDestinations.Count > 0; - } - - public override int GetDestination(HttpContext httpContext) - { - int destination; + accessControlRequestMethod = default; + var headers = httpContext.Request.Headers; - var httpMethod = httpContext.Request.Method; - var headers = httpContext.Request.Headers; - if (_supportsCorsPreflight && - HttpMethods.Equals(httpMethod, PreflightHttpMethod) && - headers.ContainsKey(HeaderNames.Origin) && - headers.TryGetValue(HeaderNames.AccessControlRequestMethod, out var accessControlRequestMethod) && - !StringValues.IsNullOrEmpty(accessControlRequestMethod)) - { - return _corsPreflightDestinations != null && - _corsPreflightDestinations.TryGetValue(accessControlRequestMethod, out destination) - ? destination - : _corsPreflightExitDestination; - } - - return _destinations != null && - _destinations.TryGetValue(httpMethod, out destination) ? destination : _exitDestination; - } + return HttpMethods.Equals(httpMethod, PreflightHttpMethod) && + headers.ContainsKey(HeaderNames.Origin) && + headers.TryGetValue(HeaderNames.AccessControlRequestMethod, out accessControlRequestMethod) && + !StringValues.IsNullOrEmpty(accessControlRequestMethod); } private class HttpMethodMetadataEndpointComparer : EndpointMetadataComparer diff --git a/src/Http/Routing/src/Matching/HttpMethodSingleEntryPolicyJumpTable.cs b/src/Http/Routing/src/Matching/HttpMethodSingleEntryPolicyJumpTable.cs new file mode 100644 index 000000000000..a114b373da57 --- /dev/null +++ b/src/Http/Routing/src/Matching/HttpMethodSingleEntryPolicyJumpTable.cs @@ -0,0 +1,45 @@ +// 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.Http; + +namespace Microsoft.AspNetCore.Routing.Matching +{ + internal sealed class HttpMethodSingleEntryPolicyJumpTable : PolicyJumpTable + { + private readonly int _exitDestination; + private readonly string _method; + private readonly int _destination; + private readonly int _corsPreflightExitDestination; + private readonly int _corsPreflightDestination; + + private readonly bool _supportsCorsPreflight; + + public HttpMethodSingleEntryPolicyJumpTable( + int exitDestination, + string method, + int destination, + bool supportsCorsPreflight, + int corsPreflightExitDestination, + int corsPreflightDestination) + { + _exitDestination = exitDestination; + _method = method; + _destination = destination; + _supportsCorsPreflight = supportsCorsPreflight; + _corsPreflightExitDestination = corsPreflightExitDestination; + _corsPreflightDestination = corsPreflightDestination; + } + + public override int GetDestination(HttpContext httpContext) + { + var httpMethod = httpContext.Request.Method; + if (_supportsCorsPreflight && HttpMethodMatcherPolicy.IsCorsPreflightRequest(httpContext, httpMethod, out var accessControlRequestMethod)) + { + return HttpMethods.Equals(accessControlRequestMethod, _method) ? _corsPreflightDestination : _corsPreflightExitDestination; + } + + return HttpMethods.Equals(httpMethod, _method) ? _destination : _exitDestination; + } + } +} diff --git a/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs b/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs index 5f219e329abe..fbb8cecc69ca 100644 --- a/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs +++ b/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs @@ -84,14 +84,16 @@ public async Task NotMatch_HttpMethod_CORS_Preflight() Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, httpContext.GetEndpoint().DisplayName); } - [Fact] - public async Task Match_HttpMethod_CaseInsensitive() + [Theory] + [InlineData("GeT", "GET")] + [InlineData("unKNOWN", "UNKNOWN")] + public async Task Match_HttpMethod_CaseInsensitive(string endpointMethod, string requestMethod) { // Arrange - var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", }); + var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { endpointMethod, }); var matcher = CreateMatcher(endpoint); - var httpContext = CreateContext("/hello", "GET"); + var httpContext = CreateContext("/hello", requestMethod); // Act await matcher.MatchAsync(httpContext); @@ -100,14 +102,16 @@ public async Task Match_HttpMethod_CaseInsensitive() MatcherAssert.AssertMatch(httpContext, endpoint); } - [Fact] - public async Task Match_HttpMethod_CaseInsensitive_CORS_Preflight() + [Theory] + [InlineData("GeT", "GET")] + [InlineData("unKNOWN", "UNKNOWN")] + public async Task Match_HttpMethod_CaseInsensitive_CORS_Preflight(string endpointMethod, string requestMethod) { // Arrange - var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", }, acceptCorsPreflight: true); + var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { endpointMethod, }, acceptCorsPreflight: true); var matcher = CreateMatcher(endpoint); - var httpContext = CreateContext("/hello", "GET", corsPreflight: true); + var httpContext = CreateContext("/hello", requestMethod, corsPreflight: true); // Act await matcher.MatchAsync(httpContext); diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/AspNetConventionsConfigureOptions.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/AspNetConventionsConfigureOptions.cs index 481f96fdc392..9699941961d6 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/AspNetConventionsConfigureOptions.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/AspNetConventionsConfigureOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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 IdentityServer4.Configuration; @@ -16,6 +16,7 @@ public void Configure(IdentityServerOptions options) options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; options.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme; + options.UserInteraction.ErrorUrl = "/Home"; } } -} \ No newline at end of file +} diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureApiScopes.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureApiScopes.cs new file mode 100644 index 000000000000..468c9700d72d --- /dev/null +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureApiScopes.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 IdentityServer4.Models; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration +{ + internal class ConfigureApiScopes : IPostConfigureOptions + { + public void PostConfigure(string name, ApiAuthorizationOptions options) + { + AddResourceScopesToApiScopes(options); + } + + private void AddResourceScopesToApiScopes(ApiAuthorizationOptions options) + { + foreach (var resource in options.ApiResources) + { + foreach (var scope in resource.Scopes) + { + if (!options.ApiScopes.ContainsScope(scope)) + { + options.ApiScopes.Add(new ApiScope(scope)); + } + } + } + } + } +} diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureClientScopes.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureClientScopes.cs index e09666e15c69..9f402224c4db 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureClientScopes.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureClientScopes.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -103,7 +103,7 @@ private static void AddScopes(ApiResource resource, Client client) { foreach (var scope in resource.Scopes) { - client.AllowedScopes.Add(scope.Name); + client.AllowedScopes.Add(scope); } } } diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureSigningCredentials.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureSigningCredentials.cs index bf2b4ae67ae4..69b511587752 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureSigningCredentials.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Configuration/ConfigureSigningCredentials.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -35,13 +35,26 @@ public ConfigureSigningCredentials( public void Configure(ApiAuthorizationOptions options) { var key = LoadKey(); - options.SigningCredential = key; + if (key != null) + { + options.SigningCredential = key; + } } public SigningCredentials LoadKey() { + // We can't know for sure if there was a configuration section explicitly defined. + // Check if the current configuration has any children and avoid failing if that's the case. + // This will avoid failing when no configuration has been specified but will still fail if partial data + // was defined. + if (!_configuration.GetChildren().Any()) + { + return null; + } + var key = new KeyDefinition(); _configuration.Bind(key); + switch (key.Type) { case KeySources.Development: @@ -66,8 +79,6 @@ public SigningCredentials LoadKey() } _logger.LogInformation($"Loading certificate with subject '{key.Name}' in '{key.StoreLocation}\\{key.StoreName}'."); return new SigningCredentials(new X509SecurityKey(SigningKeysLoader.LoadFromStoreCert(key.Name, key.StoreName, storeLocation, GetCurrentTime())), "RS256"); - case null: - throw new InvalidOperationException($"Key type not specified."); default: throw new InvalidOperationException($"Invalid key type '{key.Type ?? "(null)"}'."); } diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Extensions/AutoRedirectEndSessionEndpoint.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Extensions/AutoRedirectEndSessionEndpoint.cs index b4163d5d8a04..3a6ba0789bee 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Extensions/AutoRedirectEndSessionEndpoint.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Extensions/AutoRedirectEndSessionEndpoint.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using IdentityServer4.Configuration; using IdentityServer4.Endpoints.Results; +using IdentityServer4.Extensions; using IdentityServer4.Hosting; using IdentityServer4.Services; using IdentityServer4.Validation; diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/IdentityServerBuilderConfigurationExtensions.cs b/src/Identity/ApiAuthorization.IdentityServer/src/IdentityServerBuilderConfigurationExtensions.cs index b6e2fac1c8bc..6848d901a783 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/IdentityServerBuilderConfigurationExtensions.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/IdentityServerBuilderConfigurationExtensions.cs @@ -93,6 +93,7 @@ public static IIdentityServerBuilder AddApiResources( IConfiguration configuration) { builder.ConfigureReplacedServices(); + builder.AddApiScopes(); builder.AddInMemoryApiResources(Enumerable.Empty()); builder.Services.TryAddEnumerable( ServiceDescriptor.Singleton, ConfigureApiResources>(sp => @@ -114,6 +115,23 @@ public static IIdentityServerBuilder AddApiResources( return builder; } + /// Adds API scopes from the defined resources to the list of API scopes + internal static IIdentityServerBuilder AddApiScopes(this IIdentityServerBuilder builder) + { + // We take over the setup for the API resources as Identity Server registers the enumerable as a singleton + // and that prevents normal composition. + builder.Services.AddSingleton>(sp => + { + var options = sp.GetRequiredService>(); + return options.Value.ApiScopes; + }); + + builder.Services.TryAddEnumerable( + ServiceDescriptor.Singleton, ConfigureApiScopes>()); + + return builder; + } + /// /// Adds identity resources from the default configuration to the server using the key /// IdentityServer:Resources @@ -229,14 +247,14 @@ public static IIdentityServerBuilder AddSigningCredentials( builder.Services.AddSingleton(sp => { var options = sp.GetRequiredService>(); - return new DefaultSigningCredentialsStore(options.Value.SigningCredential); + return new InMemorySigningCredentialsStore(options.Value.SigningCredential); }); // We take over the setup for the validation keys store as Identity Server registers a singleton builder.Services.AddSingleton(sp => { var options = sp.GetRequiredService>(); - return new DefaultValidationKeysStore(new[] + return new InMemoryValidationKeysStore(new[] { new SecurityKeyInfo { diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiAuthorizationOptions.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiAuthorizationOptions.cs index f6725f39477f..0f05a8209b2b 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiAuthorizationOptions.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiAuthorizationOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.IdentityModel.Tokens; @@ -32,6 +32,12 @@ public class ApiAuthorizationOptions public ApiResourceCollection ApiResources { get; set; } = new ApiResourceCollection(); + /// + /// Gets or sets the . + /// + public ApiScopeCollection ApiScopes { get; set; } = + new ApiScopeCollection(); + /// /// Gets or sets the . /// diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiResourceBuilder.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiResourceBuilder.cs index 930b9bdb7792..0f51c08de702 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiResourceBuilder.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiResourceBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -24,7 +24,8 @@ public static ApiResourceBuilder ApiResource(string name) { var apiResource = new ApiResource(name); return new ApiResourceBuilder(apiResource) - .WithApplicationProfile(ApplicationProfiles.API); + .WithApplicationProfile(ApplicationProfiles.API) + .WithScopes(name); } /// @@ -75,12 +76,12 @@ public ApiResourceBuilder WithScopes(params string[] resourceScopes) { foreach (var scope in resourceScopes) { - if (_apiResource.Scopes.Any(s => s.Name == scope)) + if (_apiResource.Scopes.Any(s => s == scope)) { continue; } - _apiResource.Scopes.Add(new Scope(scope)); + _apiResource.Scopes.Add(scope); } return this; diff --git a/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiScopeCollection.cs b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiScopeCollection.cs new file mode 100644 index 000000000000..f4265666b797 --- /dev/null +++ b/src/Identity/ApiAuthorization.IdentityServer/src/Options/ApiScopeCollection.cs @@ -0,0 +1,82 @@ +// 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.Collections.ObjectModel; +using IdentityServer4.Models; + +namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer +{ + public class ApiScopeCollection : Collection + { + /// + /// Initializes a new instance of . + /// + public ApiScopeCollection() + { + } + + /// + /// Initializes a new instance of with the given + /// API scopes in . + /// + /// The initial list of . + public ApiScopeCollection(IList list) : base(list) + { + } + + /// + /// Gets an API resource given its name. + /// + /// The name of the . + /// The . + public ApiScope this[string key] + { + get + { + for (int i = 0; i < Items.Count; i++) + { + var candidate = Items[i]; + if (string.Equals(candidate.Name, key, StringComparison.Ordinal)) + { + return candidate; + } + } + + throw new InvalidOperationException($"ApiScope '{key}' not found."); + } + } + + /// + /// Gets whether a given scope is defined or not. + /// + /// The name of the . + /// true when the scope is defined; false otherwise. + public bool ContainsScope(string key) + { + for (int i = 0; i < Items.Count; i++) + { + var candidate = Items[i]; + if (string.Equals(candidate.Name, key, StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + + /// + /// Adds the scopes in to the collection. + /// + /// The list of to add. + public void AddRange(params ApiScope[] scopes) + { + foreach (var resource in scopes) + { + Add(resource); + } + } + } +} diff --git a/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureApiResourcesTests.cs b/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureApiResourcesTests.cs index 96ad06f01ede..00a5e83b2407 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureApiResourcesTests.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureApiResourcesTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.ApiAuthorization.IdentityServer.Configuration; @@ -33,7 +33,7 @@ public void GetApiResources_ReadsApisFromConfiguration() var resource = Assert.Single(resources); var scope = Assert.Single(resource.Scopes); Assert.Equal("MyAPI", resource.Name); - Assert.Equal("MyAPI", scope.Name); + Assert.Equal("MyAPI", scope); } [Fact] @@ -59,7 +59,7 @@ public void GetApiResources_ReadsApiScopesFromConfiguration() Assert.Equal("MyAPI", resource.Name); Assert.NotNull(resource.Scopes); Assert.Equal(3, resource.Scopes.Count); - Assert.Equal(expectedScopes, resource.Scopes.Select(s => s.Name).ToArray()); + Assert.Equal(expectedScopes, resource.Scopes.Select(s => s).ToArray()); } [Fact] @@ -83,7 +83,7 @@ public void GetApiResources_DetectsLocallyRegisteredApis() var resource = Assert.Single(resources); var scope = Assert.Single(resource.Scopes); Assert.Equal("MyAPI", resource.Name); - Assert.Equal("MyAPI", scope.Name); + Assert.Equal("MyAPI", scope); } [Fact] @@ -109,7 +109,7 @@ public void Configure_AddsResourcesToExistingResourceList() var resource = Assert.Single(options.ApiResources); var scope = Assert.Single(resource.Scopes); Assert.Equal("MyAPI", resource.Name); - Assert.Equal("MyAPI", scope.Name); + Assert.Equal("MyAPI", scope); } private class TestLocalApiDescriptor : IIdentityServerJwtDescriptor diff --git a/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureSigningCredentialsTests.cs b/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureSigningCredentialsTests.cs index dda0233d77f7..2ec53f7a53f5 100644 --- a/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureSigningCredentialsTests.cs +++ b/src/Identity/ApiAuthorization.IdentityServer/test/Configuration/ConfigureSigningCredentialsTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Collections.Generic; @@ -22,6 +22,42 @@ public class ConfigureSigningCredentialsTests UnsafeEphemeralKeySet : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.PersistKeySet : X509KeyStorageFlags.DefaultKeySet); + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.CLR)] + public void Configure_NoOpsWhenConfigurationIsEmpty() + { + var expectedKeyPath = Path.Combine(Directory.GetCurrentDirectory(), "./testkey.json"); + try + { + // Arrange + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary() + { + }).Build(); + + var configureSigningCredentials = new ConfigureSigningCredentials( + configuration, + new TestLogger()); + + var options = new ApiAuthorizationOptions(); + + // Act + configureSigningCredentials.Configure(options); + + // Assert + Assert.NotNull(options); + Assert.False(File.Exists(expectedKeyPath)); + Assert.Null(options.SigningCredential); + } + finally + { + if (File.Exists(expectedKeyPath)) + { + File.Delete(expectedKeyPath); + } + } + } + [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] public void Configure_AddsDevelopmentKeyFromConfiguration() diff --git a/src/Mvc/Mvc.Core/src/ControllerBase.cs b/src/Mvc/Mvc.Core/src/ControllerBase.cs index b24e67a7c631..5903c4126b18 100644 --- a/src/Mvc/Mvc.Core/src/ControllerBase.cs +++ b/src/Mvc/Mvc.Core/src/ControllerBase.cs @@ -1886,13 +1886,29 @@ public virtual ObjectResult Problem( string title = null, string type = null) { - var problemDetails = ProblemDetailsFactory.CreateProblemDetails( - HttpContext, - statusCode: statusCode ?? 500, - title: title, - type: type, - detail: detail, - instance: instance); + ProblemDetails problemDetails; + if (ProblemDetailsFactory == null) + { + // ProblemDetailsFactory may be null in unit testing scenarios. Improvise to make this more testable. + problemDetails = new ProblemDetails + { + Detail = detail, + Instance = instance, + Status = statusCode ?? 500, + Title = title, + Type = type, + }; + } + else + { + problemDetails = ProblemDetailsFactory.CreateProblemDetails( + HttpContext, + statusCode: statusCode ?? 500, + title: title, + type: type, + detail: detail, + instance: instance); + } return new ObjectResult(problemDetails) { @@ -1958,14 +1974,30 @@ public virtual ActionResult ValidationProblem( { modelStateDictionary ??= ModelState; - var validationProblem = ProblemDetailsFactory.CreateValidationProblemDetails( - HttpContext, - modelStateDictionary, - statusCode: statusCode, - title: title, - type: type, - detail: detail, - instance: instance); + ValidationProblemDetails validationProblem; + if (ProblemDetailsFactory == null) + { + // ProblemDetailsFactory may be null in unit testing scenarios. Improvise to make this more testable. + validationProblem = new ValidationProblemDetails(modelStateDictionary) + { + Detail = detail, + Instance = instance, + Status = statusCode, + Title = title, + Type = type, + }; + } + else + { + validationProblem = ProblemDetailsFactory?.CreateValidationProblemDetails( + HttpContext, + modelStateDictionary, + statusCode: statusCode, + title: title, + type: type, + detail: detail, + instance: instance); + } if (validationProblem.Status == 400) { diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs index a2b94da1188e..c12f5af2a381 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs @@ -63,6 +63,7 @@ public void Configure(MvcOptions options) options.ModelBinderProviders.Add(new HeaderModelBinderProvider()); options.ModelBinderProviders.Add(new FloatingPointTypeModelBinderProvider()); options.ModelBinderProviders.Add(new EnumTypeModelBinderProvider(options)); + options.ModelBinderProviders.Add(new DateTimeModelBinderProvider()); options.ModelBinderProviders.Add(new SimpleTypeModelBinderProvider()); options.ModelBinderProviders.Add(new CancellationTokenModelBinderProvider()); options.ModelBinderProviders.Add(new ByteArrayModelBinderProvider()); diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinder.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinder.cs new file mode 100644 index 000000000000..70f7403b064a --- /dev/null +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinder.cs @@ -0,0 +1,105 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders +{ + /// + /// An for and nullable models. + /// + public class DateTimeModelBinder : IModelBinder + { + private readonly DateTimeStyles _supportedStyles; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of . + /// + /// The . + /// The . + public DateTimeModelBinder(DateTimeStyles supportedStyles, ILoggerFactory loggerFactory) + { + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + _supportedStyles = supportedStyles; + _logger = loggerFactory.CreateLogger(); + } + + /// + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext == null) + { + throw new ArgumentNullException(nameof(bindingContext)); + } + + _logger.AttemptingToBindModel(bindingContext); + + var modelName = bindingContext.ModelName; + var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); + if (valueProviderResult == ValueProviderResult.None) + { + _logger.FoundNoValueInRequest(bindingContext); + + // no entry + _logger.DoneAttemptingToBindModel(bindingContext); + return Task.CompletedTask; + } + + var modelState = bindingContext.ModelState; + modelState.SetModelValue(modelName, valueProviderResult); + + var metadata = bindingContext.ModelMetadata; + var type = metadata.UnderlyingOrModelType; + try + { + var value = valueProviderResult.FirstValue; + + object model; + if (string.IsNullOrWhiteSpace(value)) + { + // Parse() method trims the value (with common DateTimeSyles) then throws if the result is empty. + model = null; + } + else if (type == typeof(DateTime)) + { + model = DateTime.Parse(value, valueProviderResult.Culture, _supportedStyles); + } + else + { + throw new NotSupportedException(); + } + + // When converting value, a null model may indicate a failed conversion for an otherwise required + // model (can't set a ValueType to null). This detects if a null model value is acceptable given the + // current bindingContext. If not, an error is logged. + if (model == null && !metadata.IsReferenceOrNullableType) + { + modelState.TryAddModelError( + modelName, + metadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor( + valueProviderResult.ToString())); + } + else + { + bindingContext.Result = ModelBindingResult.Success(model); + } + } + catch (Exception exception) + { + // Conversion failed. + modelState.TryAddModelError(modelName, exception, metadata); + } + + _logger.DoneAttemptingToBindModel(bindingContext); + return Task.CompletedTask; + } + } +} diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinderProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinderProvider.cs new file mode 100644 index 000000000000..aad9323f193d --- /dev/null +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DateTimeModelBinderProvider.cs @@ -0,0 +1,36 @@ +// 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.Globalization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders +{ + /// + /// An for binding and nullable models. + /// + public class DateTimeModelBinderProvider : IModelBinderProvider + { + internal static readonly DateTimeStyles SupportedStyles = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AllowWhiteSpaces; + + /// + public IModelBinder GetBinder(ModelBinderProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var modelType = context.Metadata.UnderlyingOrModelType; + if (modelType == typeof(DateTime)) + { + var loggerFactory = context.Services.GetRequiredService(); + return new DateTimeModelBinder(SupportedStyles, loggerFactory); + } + + return null; + } + } +} diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DecimalModelBinder.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DecimalModelBinder.cs index b8f27116b568..83001eb70627 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DecimalModelBinder.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DecimalModelBinder.cs @@ -63,7 +63,6 @@ public Task BindModelAsync(ModelBindingContext bindingContext) try { var value = valueProviderResult.FirstValue; - var culture = valueProviderResult.Culture; object model; if (string.IsNullOrWhiteSpace(value)) @@ -73,7 +72,7 @@ public Task BindModelAsync(ModelBindingContext bindingContext) } else if (type == typeof(decimal)) { - model = decimal.Parse(value, _supportedStyles, culture); + model = decimal.Parse(value, _supportedStyles, valueProviderResult.Culture); } else { diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DoubleModelBinder.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DoubleModelBinder.cs index 64dd08301a4f..27e7417bd946 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DoubleModelBinder.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/DoubleModelBinder.cs @@ -63,7 +63,6 @@ public Task BindModelAsync(ModelBindingContext bindingContext) try { var value = valueProviderResult.FirstValue; - var culture = valueProviderResult.Culture; object model; if (string.IsNullOrWhiteSpace(value)) @@ -73,7 +72,7 @@ public Task BindModelAsync(ModelBindingContext bindingContext) } else if (type == typeof(double)) { - model = double.Parse(value, _supportedStyles, culture); + model = double.Parse(value, _supportedStyles, valueProviderResult.Culture); } else { diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FloatModelBinder.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FloatModelBinder.cs index 09a150213b08..733d8e28c92c 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FloatModelBinder.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FloatModelBinder.cs @@ -63,7 +63,6 @@ public Task BindModelAsync(ModelBindingContext bindingContext) try { var value = valueProviderResult.FirstValue; - var culture = valueProviderResult.Culture; object model; if (string.IsNullOrWhiteSpace(value)) @@ -73,7 +72,7 @@ public Task BindModelAsync(ModelBindingContext bindingContext) } else if (type == typeof(float)) { - model = float.Parse(value, _supportedStyles, culture); + model = float.Parse(value, _supportedStyles, valueProviderResult.Culture); } else { diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/SimpleTypeModelBinder.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/SimpleTypeModelBinder.cs index 8d9b1eeee99a..2bf1af977b55 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Binders/SimpleTypeModelBinder.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Binders/SimpleTypeModelBinder.cs @@ -46,6 +46,8 @@ public Task BindModelAsync(ModelBindingContext bindingContext) throw new ArgumentNullException(nameof(bindingContext)); } + _logger.AttemptingToBindModel(bindingContext); + var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { @@ -56,8 +58,6 @@ public Task BindModelAsync(ModelBindingContext bindingContext) return Task.CompletedTask; } - _logger.AttemptingToBindModel(bindingContext); - bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index b122b73175a7..cff0b37416e6 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -463,6 +463,9 @@ Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinderProvider.C Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider.ComplexTypeModelBinderProvider() -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinder +Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinderProvider +Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinderProvider.DateTimeModelBinderProvider() -> void Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DecimalModelBinder Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinder Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinderProvider @@ -1464,6 +1467,9 @@ virtual Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.ComplexTypeModelBinder(System.Collections.Generic.IDictionary propertyBinders, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) -> void ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.ComplexTypeModelBinder(System.Collections.Generic.IDictionary propertyBinders, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, bool allowValidatingTopLevelNodes) -> void ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext context) -> Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder +~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinder.BindModelAsync(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext bindingContext) -> System.Threading.Tasks.Task +~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinder.DateTimeModelBinder(System.Globalization.DateTimeStyles supportedStyles, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) -> void +~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DateTimeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext context) -> Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DecimalModelBinder.BindModelAsync(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext bindingContext) -> System.Threading.Tasks.Task ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DecimalModelBinder.DecimalModelBinder(System.Globalization.NumberStyles supportedStyles, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) -> void ~Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinder.DictionaryModelBinder(Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder keyBinder, Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder valueBinder, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) -> void diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderProviderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderProviderTest.cs new file mode 100644 index 000000000000..376d3b6765fc --- /dev/null +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderProviderTest.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders +{ + public class DateTimeModelBinderProviderTest + { + private readonly DateTimeModelBinderProvider _provider = new DateTimeModelBinderProvider(); + + [Theory] + [InlineData(typeof(string))] + [InlineData(typeof(DateTimeOffset))] + [InlineData(typeof(DateTimeOffset?))] + [InlineData(typeof(TimeSpan))] + public void Create_ForNonDateTime_ReturnsNull(Type modelType) + { + // Arrange + var context = new TestModelBinderProviderContext(modelType); + + // Act + var result = _provider.GetBinder(context); + + // Assert + Assert.Null(result); + } + + [Fact] + public void Create_ForDateTime_ReturnsBinder() + { + // Arrange + var context = new TestModelBinderProviderContext(typeof(DateTime)); + + // Act + var result = _provider.GetBinder(context); + + // Assert + Assert.IsType(result); + } + + [Fact] + public void Create_ForNullableDateTime_ReturnsBinder() + { + // Arrange + var context = new TestModelBinderProviderContext(typeof(DateTime?)); + + // Act + var result = _provider.GetBinder(context); + + // Assert + Assert.IsType(result); + } + } +} diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderTest.cs new file mode 100644 index 000000000000..2fd87e551ab1 --- /dev/null +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/DateTimeModelBinderTest.cs @@ -0,0 +1,222 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders +{ + public class DateTimeModelBinderTest + { + [Fact] + public async Task BindModel_ReturnsFailure_IfAttemptedValueCannotBeParsed() + { + // Arrange + var bindingContext = GetBindingContext(); + bindingContext.ValueProvider = new SimpleValueProvider + { + { "theModelName", "some-value" } + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.False(bindingContext.Result.IsModelSet); + } + + [Fact] + public async Task BindModel_CreatesError_IfAttemptedValueCannotBeParsed() + { + // Arrange + var message = "The value 'not a date' is not valid."; + var bindingContext = GetBindingContext(); + bindingContext.ValueProvider = new SimpleValueProvider + { + { "theModelName", "not a date" }, + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.False(bindingContext.Result.IsModelSet); + Assert.Null(bindingContext.Result.Model); + Assert.False(bindingContext.ModelState.IsValid); + + var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors); + Assert.Equal(message, error.ErrorMessage); + } + + [Fact] + public async Task BindModel_CreatesError_IfAttemptedValueCannotBeCompletelyParsed() + { + // Arrange + var bindingContext = GetBindingContext(); + bindingContext.ValueProvider = new SimpleValueProvider(new CultureInfo("en-GB")) + { + { "theModelName", "2020-08-not-a-date" } + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.False(bindingContext.Result.IsModelSet); + Assert.Null(bindingContext.Result.Model); + + var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors); + Assert.Equal("The value '2020-08-not-a-date' is not valid.", error.ErrorMessage, StringComparer.Ordinal); + Assert.Null(error.Exception); + } + + [Fact] + public async Task BindModel_ReturnsFailed_IfValueProviderEmpty() + { + // Arrange + var bindingContext = GetBindingContext(typeof(DateTime)); + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result); + Assert.Empty(bindingContext.ModelState); + } + + [Fact] + public async Task BindModel_NullableDatetime_ReturnsFailed_IfValueProviderEmpty() + { + // Arrange + var bindingContext = GetBindingContext(typeof(DateTime?)); + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result); + Assert.Empty(bindingContext.ModelState); + } + + [Theory] + [InlineData("")] + [InlineData(" \t \r\n ")] + public async Task BindModel_CreatesError_IfTrimmedAttemptedValueIsEmpty_NonNullableDestination(string value) + { + // Arrange + var message = $"The value '{value}' is invalid."; + var bindingContext = GetBindingContext(); + bindingContext.ValueProvider = new SimpleValueProvider + { + { "theModelName", value }, + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.False(bindingContext.Result.IsModelSet); + Assert.Null(bindingContext.Result.Model); + + var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors); + Assert.Equal(message, error.ErrorMessage, StringComparer.Ordinal); + Assert.Null(error.Exception); + } + + [Theory] + [InlineData("")] + [InlineData(" \t \r\n ")] + public async Task BindModel_ReturnsNull_IfTrimmedAttemptedValueIsEmpty_NullableDestination(string value) + { + // Arrange + var bindingContext = GetBindingContext(typeof(DateTime?)); + bindingContext.ValueProvider = new SimpleValueProvider + { + { "theModelName", value } + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.Null(bindingContext.Result.Model); + var entry = Assert.Single(bindingContext.ModelState); + Assert.Equal("theModelName", entry.Key); + } + + [Theory] + [InlineData(typeof(DateTime))] + [InlineData(typeof(DateTime?))] + public async Task BindModel_ReturnsModel_IfAttemptedValueIsValid(Type type) + { + // Arrange + var expected = new DateTime(2019, 06, 14, 2, 30, 4, 0, DateTimeKind.Utc); + var bindingContext = GetBindingContext(type); + bindingContext.ValueProvider = new SimpleValueProvider(new CultureInfo("fr-FR")) + { + { "theModelName", "2019-06-14T02:30:04.0000000Z" } + }; + var binder = GetBinder(); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.True(bindingContext.Result.IsModelSet); + var model = Assert.IsType(bindingContext.Result.Model); + Assert.Equal(expected, model); + Assert.Equal(DateTimeKind.Utc, model.Kind); + Assert.True(bindingContext.ModelState.ContainsKey("theModelName")); + } + + [Fact] + public async Task UsesSpecifiedStyleToParseModel() + { + // Arrange + var bindingContext = GetBindingContext(); + var expected = DateTime.Parse("2019-06-14T02:30:04.0000000Z"); + bindingContext.ValueProvider = new SimpleValueProvider(new CultureInfo("fr-FR")) + { + { "theModelName", "2019-06-14T02:30:04.0000000Z" } + }; + var binder = GetBinder(DateTimeStyles.AssumeLocal); + + // Act + await binder.BindModelAsync(bindingContext); + + // Assert + Assert.True(bindingContext.Result.IsModelSet); + var model = Assert.IsType(bindingContext.Result.Model); + Assert.Equal(expected, model); + Assert.Equal(DateTimeKind.Local, model.Kind); + Assert.True(bindingContext.ModelState.ContainsKey("theModelName")); + } + + private IModelBinder GetBinder(DateTimeStyles? dateTimeStyles = null) + { + return new DateTimeModelBinder(dateTimeStyles ?? DateTimeModelBinderProvider.SupportedStyles, NullLoggerFactory.Instance); + } + + private static DefaultModelBindingContext GetBindingContext(Type modelType = null) + { + modelType ??= typeof(DateTime); + return new DefaultModelBindingContext + { + ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType), + ModelName = "theModelName", + ModelState = new ModelStateDictionary(), + ValueProvider = new SimpleValueProvider() // empty + }; + } + } +} diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Binders/SimpleTypeModelBinderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/SimpleTypeModelBinderTest.cs index e50098b146f3..ee5c372cd095 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/Binders/SimpleTypeModelBinderTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Binders/SimpleTypeModelBinderTest.cs @@ -194,7 +194,7 @@ public async Task BindModel_EmptyValueProviderResult_ReturnsFailedAndLogsSuccess // Assert Assert.Equal(ModelBindingResult.Failed(), bindingContext.Result); Assert.Empty(bindingContext.ModelState); - Assert.Equal(2, sink.Writes.Count()); + Assert.Equal(3, sink.Writes.Count()); } [Theory] diff --git a/src/Mvc/Mvc.ViewFeatures/test/ControllerUnitTestabilityTests.cs b/src/Mvc/Mvc.ViewFeatures/test/ControllerUnitTestabilityTests.cs index 63615039b14e..00a4485b8c81 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/ControllerUnitTestabilityTests.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/ControllerUnitTestabilityTests.cs @@ -373,7 +373,7 @@ public void ControllerAcceptedAtRoute_InvokedInUnitTests() Assert.Equal(routeName, acceptedAtRouteResult.RouteName); Assert.Single(acceptedAtRouteResult.RouteValues); Assert.Equal("sample", acceptedAtRouteResult.RouteValues["route"]); - Assert.Same(value,acceptedAtRouteResult.Value); + Assert.Same(value, acceptedAtRouteResult.Value); // Arrange controller = new TestabilityController(); @@ -682,6 +682,42 @@ public void ViewComponent_WithArguments() Assert.Equal(new { Arg1 = "Hi", Arg2 = "There" }, result.Arguments); } + [Fact] + public void Problem_Works() + { + // Arrange + var detail = "Some random error"; + var controller = new TestabilityController(); + + // Act + var result = controller.Problem(detail); + + // Assert + var badRequest = Assert.IsType(result); + var problemDetails = Assert.IsType(badRequest.Value); + Assert.Equal(detail, problemDetails.Detail); + } + + [Fact] + public void ValidationProblem_Works() + { + // Arrange + var detail = "Some random error"; + var controller = new TestabilityController(); + + // Act + controller.ModelState.AddModelError("some-key", "some-error"); + var result = controller.ValidationProblem(detail); + + // Assert + var badRequest = Assert.IsType(result); + var validationProblemDetails = Assert.IsType(badRequest.Value); + Assert.Equal(detail, validationProblemDetails.Detail); + var error = Assert.Single(validationProblemDetails.Errors); + Assert.Equal("some-key", error.Key); + Assert.Equal(new[] { "some-error" }, error.Value); + } + public static IEnumerable TestabilityViewTestData { get diff --git a/src/Mvc/Mvc/test/MvcOptionsSetupTest.cs b/src/Mvc/Mvc/test/MvcOptionsSetupTest.cs index eb8d0933b48c..b0a740c1f945 100644 --- a/src/Mvc/Mvc/test/MvcOptionsSetupTest.cs +++ b/src/Mvc/Mvc/test/MvcOptionsSetupTest.cs @@ -58,6 +58,7 @@ public void Setup_SetsUpModelBinderProviders() binder => Assert.IsType(binder), binder => Assert.IsType(binder), binder => Assert.IsType(binder), + binder => Assert.IsType(binder), binder => Assert.IsType(binder), binder => Assert.IsType(binder), binder => Assert.IsType(binder), diff --git a/src/Mvc/test/Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs b/src/Mvc/test/Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs index 62f3c3eb9fa8..634ba9bcedbb 100644 --- a/src/Mvc/test/Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs +++ b/src/Mvc/test/Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; @@ -229,6 +231,91 @@ public async Task BindDecimalParameter_WithData_GetsBound() Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } + [Fact] + [ReplaceCulture("en-GB", "en-GB")] + public async Task BindDateTimeParameter_WithData_GetsBound() + { + // Arrange + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); + var parameter = new ParameterDescriptor + { + Name = "Parameter1", + ParameterType = typeof(DateTime), + BindingInfo = new BindingInfo(), + }; + + var testContext = ModelBindingTestHelper.GetTestContext(request => + { + request.QueryString = QueryString.Create("Parameter1", "2020-02-01"); + }); + + var modelState = testContext.ModelState; + + // Act + var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); + + // Assert + + // ModelBindingResult + Assert.True(modelBindingResult.IsModelSet); + + // Model + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal(new DateTime(2020, 02, 01, 0, 0, 0, DateTimeKind.Utc), model); + + // ModelState + Assert.True(modelState.IsValid); + + Assert.Single(modelState.Keys); + var key = Assert.Single(modelState.Keys); + Assert.Equal("Parameter1", key); + Assert.Equal("2020-02-01", modelState[key].AttemptedValue); + Assert.Equal("2020-02-01", modelState[key].RawValue); + Assert.Empty(modelState[key].Errors); + Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); + } + + [Fact] + [ReplaceCulture("en-GB", "en-GB")] + public async Task BindDateTimeParameter_WithDataFromBody_GetsBound() + { + // Arrange + var input = "\"2020-02-01\""; + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); + var parameter = new ParameterDescriptor + { + Name = "Parameter1", + ParameterType = typeof(DateTime), + BindingInfo = new BindingInfo + { + BindingSource = BindingSource.Body, + } + }; + + var testContext = ModelBindingTestHelper.GetTestContext(request => + { + request.Body = new MemoryStream(Encoding.UTF8.GetBytes(input)); + request.ContentType = "application/json"; + }); + + var modelState = testContext.ModelState; + + // Act + var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); + + // Assert + + // ModelBindingResult + Assert.True(modelBindingResult.IsModelSet); + + // Model + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal(new DateTime(2020, 02, 01, 0, 0, 0, DateTimeKind.Utc), model); + + // ModelState + Assert.True(modelState.IsValid); + } + [Fact] public async Task BindParameter_WithMultipleValues_GetsBoundToFirstValue() { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs index 414379d1c2a7..23df6673d35e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -16,7 +16,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("ComponentsWebAssembly_CSharp.Server.Models.ApplicationUser", b => { @@ -31,8 +31,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -44,12 +44,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -67,17 +67,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -85,34 +85,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -127,33 +135,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -161,6 +180,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -174,18 +195,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -239,12 +260,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -281,12 +302,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.cs index a1b61ddc1e1d..25d42c8dae46 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: false), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Type = table.Column(type: "TEXT", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: true), + ConsumedTime = table.Column(type: "TEXT", nullable: true), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Name = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Value = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -243,6 +248,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/ApplicationDbContextModelSnapshot.cs index 8352792068c1..3c3c91a357fa 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlLite/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using ComponentsWebAssembly_CSharp.Server.Data; using Microsoft.EntityFrameworkCore; @@ -14,7 +14,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("ComponentsWebAssembly_CSharp.Server.Models.ApplicationUser", b => { @@ -29,8 +29,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -42,12 +42,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -65,17 +65,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -83,34 +83,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -125,33 +133,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -159,6 +178,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -172,18 +193,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -237,12 +258,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -279,12 +300,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs index 9b11b6b06099..072bc519965e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -17,9 +17,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("ComponentsWebAssembly_CSharp.Server.Models.ApplicationUser", b => { @@ -34,8 +34,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -47,12 +47,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -70,17 +70,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -89,34 +89,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -131,33 +139,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -165,6 +184,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -178,18 +199,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -200,7 +221,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -224,7 +245,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -246,12 +267,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -288,12 +309,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.cs index 00ba7147171a..84ba4d1e0730 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: false), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: true), + ConsumedTime = table.Column(type: "datetime2", nullable: true), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -245,6 +250,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/ApplicationDbContextModelSnapshot.cs index 8df4c8457da6..c751e16729b8 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Data/SqlServer/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using ComponentsWebAssembly_CSharp.Server.Data; using Microsoft.EntityFrameworkCore; @@ -15,9 +15,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("ComponentsWebAssembly_CSharp.Server.Models.ApplicationUser", b => { @@ -32,8 +32,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -45,12 +45,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -68,17 +68,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -87,34 +87,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -129,33 +137,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -163,6 +182,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -176,18 +197,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -198,7 +219,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -222,7 +243,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -244,12 +265,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -286,12 +307,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/app.db b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/app.db index 1f4261428921..3eef2bb381f8 100644 Binary files a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/app.db and b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/app.db differ diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs index c17ab45e1957..259c3c7b1161 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -17,9 +17,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -34,8 +34,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -47,12 +47,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -70,17 +70,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -89,34 +89,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -131,33 +139,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -165,6 +184,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -178,18 +199,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -200,7 +221,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -224,7 +245,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -246,12 +267,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -288,12 +309,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs index ce9a727d1ae4..da38a79c964b 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: false), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: true), + ConsumedTime = table.Column(type: "datetime2", nullable: true), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -245,6 +250,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs index 0ce86894d5d3..dc7a40e862f0 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Company.WebApplication1.Data; using Microsoft.EntityFrameworkCore; @@ -15,9 +15,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -32,8 +32,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -45,12 +45,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -68,17 +68,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -87,34 +87,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -129,33 +137,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -163,6 +182,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -176,18 +197,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -198,7 +219,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -222,7 +243,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -244,12 +265,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -286,12 +307,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs index 3a8bebcf4dd6..840eccdd320f 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -16,7 +16,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -31,8 +31,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -44,12 +44,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -67,17 +67,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -85,34 +85,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -127,33 +135,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -161,6 +180,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -174,18 +195,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -239,12 +260,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -281,12 +302,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs index 8ffa93f693d4..937b7e26835f 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: false), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Type = table.Column(type: "TEXT", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: true), + ConsumedTime = table.Column(type: "TEXT", nullable: true), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Name = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Value = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -243,6 +248,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs index 52809076bf62..8d41a27030d6 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Company.WebApplication1.Data; using Microsoft.EntityFrameworkCore; @@ -14,7 +14,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -29,8 +29,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -42,12 +42,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -65,17 +65,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -83,34 +83,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -125,33 +133,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -159,6 +178,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -172,18 +193,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -237,12 +258,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -279,12 +300,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/app.db b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/app.db index 1f4261428921..3eef2bb381f8 100644 Binary files a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/app.db and b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/app.db differ diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs index c17ab45e1957..259c3c7b1161 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -17,9 +17,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -34,8 +34,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -47,12 +47,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -70,17 +70,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -89,34 +89,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -131,33 +139,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -165,6 +184,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -178,18 +199,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -200,7 +221,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -224,7 +245,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -246,12 +267,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -288,12 +309,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs index ce9a727d1ae4..da38a79c964b 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: false), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + SessionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientId = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: true), + ConsumedTime = table.Column(type: "datetime2", nullable: true), + Data = table.Column(type: "nvarchar(max)", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { @@ -245,6 +250,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs index 0ce86894d5d3..dc7a40e862f0 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLServer/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Company.WebApplication1.Data; using Microsoft.EntityFrameworkCore; @@ -15,9 +15,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -32,8 +32,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -45,12 +45,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -68,17 +68,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); @@ -87,34 +87,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("UserCode"); @@ -129,33 +137,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("ClientId") .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Expiration") .HasColumnType("datetime2"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Type") .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.HasKey("Key"); @@ -163,6 +182,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -176,18 +197,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); @@ -198,7 +219,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -222,7 +243,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .UseIdentityColumn(); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -244,12 +265,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -286,12 +307,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs index 3a8bebcf4dd6..840eccdd320f 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -16,7 +16,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -31,8 +31,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -44,12 +44,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -67,17 +67,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -85,34 +85,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -127,33 +135,44 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -161,6 +180,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -174,18 +195,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -239,12 +260,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -281,12 +302,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs index 8ffa93f693d4..937b7e26835f 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.cs @@ -11,10 +11,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoles", columns: table => new { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -25,21 +25,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUsers", columns: table => new { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false) + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -50,13 +50,15 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "DeviceCodes", columns: table => new { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) + UserCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + DeviceCode = table.Column(type: "TEXT", maxLength: 200, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: false), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -67,13 +69,16 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "PersistedGrants", columns: table => new { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) + Key = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Type = table.Column(type: "TEXT", maxLength: 50, nullable: false), + SubjectId = table.Column(type: "TEXT", maxLength: 200, nullable: true), + SessionId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + ClientId = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Description = table.Column(type: "TEXT", maxLength: 200, nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false), + Expiration = table.Column(type: "TEXT", nullable: true), + ConsumedTime = table.Column(type: "TEXT", nullable: true), + Data = table.Column(type: "TEXT", maxLength: 50000, nullable: false) }, constraints: table => { @@ -84,11 +89,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetRoleClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -105,11 +110,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserClaims", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -126,10 +131,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserLogins", columns: table => new { - LoginProvider = table.Column(maxLength: 128, nullable: false), - ProviderKey = table.Column(maxLength: 128, nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -146,8 +151,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserRoles", columns: table => new { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -170,10 +175,10 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "AspNetUserTokens", columns: table => new { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 128, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(nullable: true) + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Name = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Value = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -243,6 +248,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_PersistedGrants_SubjectId_SessionId_Type", + table: "PersistedGrants", + columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs index 52809076bf62..8d41a27030d6 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Data/SQLite/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Company.WebApplication1.Data; using Microsoft.EntityFrameworkCore; @@ -14,7 +14,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0-rc1.19455.8"); + .HasAnnotation("ProductVersion", "5.0.0-rc.1.20417.2"); modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => { @@ -29,8 +29,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -42,12 +42,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -65,17 +65,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -83,34 +83,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("DeviceCode") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .IsRequired() .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.HasKey("UserCode"); @@ -125,33 +133,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("ClientId") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedTime") + .HasColumnType("TEXT"); b.Property("CreationTime") .HasColumnType("TEXT"); b.Property("Data") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50000); + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Expiration") .HasColumnType("TEXT"); + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("SubjectId") - .HasColumnType("TEXT") - .HasMaxLength(200); + .HasMaxLength(200) + .HasColumnType("TEXT"); b.Property("Type") .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("TEXT"); b.HasKey("Key"); @@ -159,6 +178,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "ClientId", "Type"); + b.HasIndex("SubjectId", "SessionId", "Type"); + b.ToTable("PersistedGrants"); }); @@ -172,18 +193,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -237,12 +258,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") .HasColumnType("TEXT"); @@ -279,12 +300,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("TEXT"); b.Property("Value") .HasColumnType("TEXT"); diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/app.db b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/app.db index 1f4261428921..3eef2bb381f8 100644 Binary files a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/app.db and b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/app.db differ diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config index 7f9995d792c3..c16575f7519c 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config @@ -2,6 +2,7 @@ + diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RedirectionOutput.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RedirectionOutput.cpp index a3897954fa58..36ef5a2c19ce 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RedirectionOutput.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RedirectionOutput.cpp @@ -77,7 +77,18 @@ void FileRedirectionOutput::Append(const std::wstring& text) { if (m_file.is_open()) { - const auto multiByte = to_multi_byte_string(text, CP_UTF8); + auto multiByte = to_multi_byte_string(text, CP_UTF8); + + // Writing \r\n to an ostream will cause two new lines to be written rather + // than one. Change all \r\n to \n. + std::string slashRslashN = "\r\n"; + std::string slashN = "\n"; + size_t start_pos = 0; + while ((start_pos = multiByte.find(slashRslashN, start_pos)) != std::string::npos) { + multiByte.replace(start_pos, slashRslashN.length(), slashN); + start_pos += slashN.length(); + } + m_file << multiByte; } } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs index 11ea36231763..a18346f0997f 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs @@ -53,6 +53,8 @@ private async Task CheckStdoutToFile(TestVariant variant, string path) var contents = Helpers.ReadAllTextFromFile(Helpers.GetExpectedLogName(deploymentResult, _logFolderPath), Logger); Assert.Contains("TEST MESSAGE", contents); + Assert.DoesNotContain("\r\n\r\n", contents); + Assert.Contains("\r\n", contents); } // Move to separate file diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs index 815ebc284b87..aff3004fb059 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs @@ -218,21 +218,6 @@ private async Task StopAsyncAwaited() _requestBodyPipe.Reset(); } - private void Copy(in ReadOnlySequence readableBuffer, PipeWriter writableBuffer) - { - if (readableBuffer.IsSingleSegment) - { - writableBuffer.Write(readableBuffer.FirstSpan); - } - else - { - foreach (var memory in readableBuffer) - { - writableBuffer.Write(memory.Span); - } - } - } - protected override void OnReadStarted() { _pumpTask = PumpAsync(); @@ -442,7 +427,7 @@ private void ReadChunkedData(in ReadOnlySequence buffer, PipeWriter writab consumed = buffer.GetPosition(actual); examined = consumed; - Copy(buffer.Slice(0, actual), writableBuffer); + buffer.Slice(0, actual).CopyTo(writableBuffer); _inputLength -= actual; AddAndCheckObservedBytes(actual); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs index 5ec3b164b67e..0266660792fa 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs @@ -34,6 +34,8 @@ internal abstract partial class HttpProtocol : IHttpResponseControl private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("\r\nConnection: keep-alive"); private static readonly byte[] _bytesTransferEncodingChunked = Encoding.ASCII.GetBytes("\r\nTransfer-Encoding: chunked"); private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: " + Constants.ServerName); + internal const string SchemeHttp = "http"; + internal const string SchemeHttps = "https"; protected BodyControl _bodyControl; private Stack, object>> _onStarting; @@ -385,7 +387,7 @@ public void Reset() if (_scheme == null) { var tlsFeature = ConnectionFeatures?[typeof(ITlsConnectionFeature)]; - _scheme = tlsFeature != null ? "https" : "http"; + _scheme = tlsFeature != null ? SchemeHttps : SchemeHttp; } Scheme = _scheme; @@ -518,7 +520,7 @@ public virtual void OnHeader(ReadOnlySpan name, ReadOnlySpan value) HttpRequestHeaders.Append(name, value); } - public virtual void OnHeader(int index, ReadOnlySpan name, ReadOnlySpan value) + public virtual void OnHeader(int index, bool indexOnly, ReadOnlySpan name, ReadOnlySpan value) { IncrementRequestHeadersCount(); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/HPackHeaderWriter.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/HPackHeaderWriter.cs index c914595bc9c9..bcb67975e5eb 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/HPackHeaderWriter.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/HPackHeaderWriter.cs @@ -151,13 +151,15 @@ private static HeaderEncodingHint ResolveHeaderEncodingHint(int staticTableId, s private static bool IsSensitive(int staticTableIndex, string name) { // Set-Cookie could contain sensitive data. - if (staticTableIndex == H2StaticTable.SetCookie) + switch (staticTableIndex) { - return true; - } - if (string.Equals(name, "Content-Disposition", StringComparison.OrdinalIgnoreCase)) - { - return true; + case H2StaticTable.SetCookie: + case H2StaticTable.ContentDisposition: + return true; + case -1: + // Content-Disposition currently isn't a known header so a + // static index probably won't be specified. + return string.Equals(name, "Content-Disposition", StringComparison.OrdinalIgnoreCase); } return false; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs index 3040c54107af..b63f4a4e7cb2 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs @@ -1226,12 +1226,9 @@ private void UpdateConnectionState() } } - // We can't throw a Http2StreamErrorException here, it interrupts the header decompression state and may corrupt subsequent header frames on other streams. - // For now these either need to be connection errors or BadRequests. If we want to downgrade any of them to stream errors later then we need to - // rework the flow so that the remaining headers are drained and the decompression state is maintained. public void OnHeader(ReadOnlySpan name, ReadOnlySpan value) { - OnHeaderCore(index: null, name, value); + OnHeaderCore(index: null, indexedValue: false, name, value); } public void OnStaticIndexedHeader(int index) @@ -1239,20 +1236,20 @@ public void OnStaticIndexedHeader(int index) Debug.Assert(index <= H2StaticTable.Count); ref readonly var entry = ref H2StaticTable.Get(index - 1); - OnHeaderCore(index, entry.Name, entry.Value); + OnHeaderCore(index, indexedValue: true, entry.Name, entry.Value); } public void OnStaticIndexedHeader(int index, ReadOnlySpan value) { Debug.Assert(index <= H2StaticTable.Count); - OnHeaderCore(index, H2StaticTable.Get(index - 1).Name, value); + OnHeaderCore(index, indexedValue: false, H2StaticTable.Get(index - 1).Name, value); } // We can't throw a Http2StreamErrorException here, it interrupts the header decompression state and may corrupt subsequent header frames on other streams. // For now these either need to be connection errors or BadRequests. If we want to downgrade any of them to stream errors later then we need to // rework the flow so that the remaining headers are drained and the decompression state is maintained. - private void OnHeaderCore(int? index, ReadOnlySpan name, ReadOnlySpan value) + private void OnHeaderCore(int? index, bool indexedValue, ReadOnlySpan name, ReadOnlySpan value) { // https://tools.ietf.org/html/rfc7540#section-6.5.2 // "The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an overhead of 32 octets for each header field."; @@ -1283,7 +1280,7 @@ private void OnHeaderCore(int? index, ReadOnlySpan name, ReadOnlySpan WriteResponseTrailers(int streamId, HttpResponseTrailers headers) + public ValueTask WriteResponseTrailersAsync(int streamId, HttpResponseTrailers headers) { lock (_writeLock) { @@ -256,6 +256,9 @@ private void FinishWritingHeaders(int streamId, int payloadLength, bool done) public ValueTask WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, in ReadOnlySequence data, bool endStream, bool firstWrite, bool forceFlush) { + // Logic in this method is replicated in WriteDataAndTrailersAsync. + // Changes here may need to be mirrored in WriteDataAndTrailersAsync. + // The Length property of a ReadOnlySequence can be expensive, so we cache the value. var dataLength = data.Length; @@ -286,6 +289,43 @@ public ValueTask WriteDataAsync(int streamId, StreamOutputFlowContr } } + public ValueTask WriteDataAndTrailersAsync(int streamId, StreamOutputFlowControl flowControl, in ReadOnlySequence data, bool firstWrite, HttpResponseTrailers headers) + { + // This method combines WriteDataAsync and WriteResponseTrailers. + // Changes here may need to be mirrored in WriteDataAsync. + + // The Length property of a ReadOnlySequence can be expensive, so we cache the value. + var dataLength = data.Length; + + lock (_writeLock) + { + if (_completed || flowControl.IsAborted) + { + return default; + } + + // Zero-length data frames are allowed to be sent immediately even if there is no space available in the flow control window. + // https://httpwg.org/specs/rfc7540.html#rfc.section.6.9.1 + if (dataLength != 0 && dataLength > flowControl.Available) + { + return WriteDataAndTrailersAsyncCore(this, streamId, flowControl, data, dataLength, firstWrite, headers); + } + + // This cast is safe since if dataLength would overflow an int, it's guaranteed to be greater than the available flow control window. + flowControl.Advance((int)dataLength); + WriteDataUnsynchronized(streamId, data, dataLength, endStream: false); + + return WriteResponseTrailersAsync(streamId, headers); + } + + static async ValueTask WriteDataAndTrailersAsyncCore(Http2FrameWriter writer, int streamId, StreamOutputFlowControl flowControl, ReadOnlySequence data, long dataLength, bool firstWrite, HttpResponseTrailers headers) + { + await writer.WriteDataAsync(streamId, flowControl, data, dataLength, endStream: false, firstWrite); + + return await writer.WriteResponseTrailersAsync(streamId, headers); + } + } + /* Padding is not implemented +---------------+ |Pad Length? (8)| @@ -317,10 +357,7 @@ private void WriteDataUnsynchronized(int streamId, in ReadOnlySequence dat WriteHeaderUnsynchronized(); - foreach (var buffer in data) - { - _outputWriter.Write(buffer.Span); - } + data.CopyTo(_outputWriter); // Plus padding return; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs index a548ae4fc116..ede4621cce68 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs @@ -423,16 +423,19 @@ private async ValueTask ProcessDataWrites() { // Output is ending and there are trailers to write // Write any remaining content then write trailers - if (readResult.Buffer.Length > 0) - { - // Only flush if required (i.e. content length exceeds flow control availability) - // Writing remaining content without flushing allows content and trailers to be sent in the same packet - await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream: false, firstWrite, forceFlush: false); - } _stream.ResponseTrailers.SetReadOnly(); _stream.DecrementActiveClientStreamCount(); - flushResult = await _frameWriter.WriteResponseTrailers(StreamId, _stream.ResponseTrailers); + + if (readResult.Buffer.Length > 0) + { + // It is faster to write data and trailers together. Locking once reduces lock contention. + flushResult = await _frameWriter.WriteDataAndTrailersAsync(StreamId, _flowControl, readResult.Buffer, firstWrite, _stream.ResponseTrailers); + } + else + { + flushResult = await _frameWriter.WriteResponseTrailersAsync(StreamId, _stream.ResponseTrailers); + } } else if (readResult.IsCompleted && _streamEnded) { diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index 613683ab9843..84b3e2874308 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.IO.Pipelines; +using System.Net.Http.HPack; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -15,6 +16,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using HttpMethods = Microsoft.AspNetCore.Http.HttpMethods; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { @@ -205,7 +207,8 @@ private bool TryValidatePseudoHeaders() _httpVersion = Http.HttpVersion.Http2; - if (!TryValidateMethod()) + // Method could already have been set from :method static table index + if (Method == HttpMethod.None && !TryValidateMethod()) { return false; } @@ -237,7 +240,9 @@ private bool TryValidatePseudoHeaders() // - That said, we shouldn't allow arbitrary values or use them to populate Request.Scheme, right? // - For now we'll restrict it to http/s and require it match the transport. // - We'll need to find some concrete scenarios to warrant unblocking this. - if (!string.Equals(HttpRequestHeaders.HeaderScheme, Scheme, StringComparison.OrdinalIgnoreCase)) + var headerScheme = HttpRequestHeaders.HeaderScheme.ToString(); + if (!ReferenceEquals(headerScheme, Scheme) && + !string.Equals(headerScheme, Scheme, StringComparison.OrdinalIgnoreCase)) { ResetAndAbort(new ConnectionAbortedException( CoreStrings.FormatHttp2StreamErrorSchemeMismatch(HttpRequestHeaders.HeaderScheme, Scheme)), Http2ErrorCode.PROTOCOL_ERROR); @@ -435,10 +440,7 @@ public Task OnDataAsync(Http2Frame dataFrame, in ReadOnlySequence payload) // Ignore data frames for aborted streams, but only after counting them for purposes of connection level flow control. if (!IsAborted) { - foreach (var segment in dataPayload) - { - RequestBodyPipe.Writer.Write(segment.Span); - } + dataPayload.CopyTo(RequestBodyPipe.Writer); // If the stream is completed go ahead and call RequestBodyPipe.Writer.Complete(). // Data will still be available to the reader. @@ -620,9 +622,33 @@ private enum StreamCompletionFlags Aborted = 4, } - public override void OnHeader(int index, ReadOnlySpan name, ReadOnlySpan value) + public override void OnHeader(int index, bool indexedValue, ReadOnlySpan name, ReadOnlySpan value) { - base.OnHeader(index, name, value); + base.OnHeader(index, indexedValue, name, value); + + if (indexedValue) + { + // Special case setting headers when the value is indexed for performance. + switch (index) + { + case H2StaticTable.MethodGet: + HttpRequestHeaders.HeaderMethod = HttpMethods.Get; + Method = HttpMethod.Get; + _methodText = HttpMethods.Get; + return; + case H2StaticTable.MethodPost: + HttpRequestHeaders.HeaderMethod = HttpMethods.Post; + Method = HttpMethod.Post; + _methodText = HttpMethods.Post; + return; + case H2StaticTable.SchemeHttp: + HttpRequestHeaders.HeaderScheme = SchemeHttp; + return; + case H2StaticTable.SchemeHttps: + HttpRequestHeaders.HeaderScheme = SchemeHttps; + return; + } + } // HPack append will return false if the index is not a known request header. // For example, someone could send the index of "Server" (a response header) in the request. diff --git a/src/Servers/Kestrel/Core/src/ListenOptions.cs b/src/Servers/Kestrel/Core/src/ListenOptions.cs index bc90bb2fbddd..fb049d102afd 100644 --- a/src/Servers/Kestrel/Core/src/ListenOptions.cs +++ b/src/Servers/Kestrel/Core/src/ListenOptions.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; namespace Microsoft.AspNetCore.Server.Kestrel.Core { @@ -84,7 +85,7 @@ internal string Scheme { get { - return IsTls ? "https" : "http"; + return IsTls ? HttpProtocol.SchemeHttps : HttpProtocol.SchemeHttp; } } diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Client/SocketConnectionFactory.cs b/src/Servers/Kestrel/Transport.Sockets/src/Client/SocketConnectionFactory.cs index 6f95d242a591..a3275335a6e2 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Client/SocketConnectionFactory.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Client/SocketConnectionFactory.cs @@ -63,7 +63,8 @@ public async ValueTask ConnectAsync(EndPoint endpoint, Cancel _trace, _options.MaxReadBufferSize, _options.MaxWriteBufferSize, - _options.WaitForDataBeforeAllocatingBuffer); + _options.WaitForDataBeforeAllocatingBuffer, + _options.UnsafePreferInlineScheduling); socketConnection.Start(); return socketConnection; diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index d8f6146d37e5..73e5e6f1e4c8 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -37,11 +37,12 @@ internal sealed class SocketConnection : TransportConnection internal SocketConnection(Socket socket, MemoryPool memoryPool, - PipeScheduler scheduler, + PipeScheduler transportScheduler, ISocketsTrace trace, long? maxReadBufferSize = null, long? maxWriteBufferSize = null, - bool waitForData = true) + bool waitForData = true, + bool useInlineSchedulers = false) { Debug.Assert(socket != null); Debug.Assert(memoryPool != null); @@ -60,7 +61,15 @@ internal SocketConnection(Socket socket, // On *nix platforms, Sockets already dispatches to the ThreadPool. // Yes, the IOQueues are still used for the PipeSchedulers. This is intentional. // https://github.com/aspnet/KestrelHttpServer/issues/2573 - var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline; + var awaiterScheduler = IsWindows ? transportScheduler : PipeScheduler.Inline; + + var applicationScheduler = PipeScheduler.ThreadPool; + if (useInlineSchedulers) + { + transportScheduler = PipeScheduler.Inline; + awaiterScheduler = PipeScheduler.Inline; + applicationScheduler = PipeScheduler.Inline; + } _receiver = new SocketReceiver(_socket, awaiterScheduler); _sender = new SocketSender(_socket, awaiterScheduler); @@ -68,8 +77,8 @@ internal SocketConnection(Socket socket, maxReadBufferSize ??= 0; maxWriteBufferSize ??= 0; - var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); - var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); + var inputOptions = new PipeOptions(MemoryPool, applicationScheduler, transportScheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); + var outputOptions = new PipeOptions(MemoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs index a98ec0436315..103a06c4d9e1 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs @@ -124,7 +124,8 @@ public async ValueTask AcceptAsync(CancellationToken cancella } var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace, - _options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer); + _options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer, + _options.UnsafePreferInlineScheduling); connection.Start(); diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs index 957876ca5953..dc48442f6a2a 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs @@ -44,6 +44,18 @@ public class SocketTransportOptions public long? MaxWriteBufferSize { get; set; } = 64 * 1024; + /// + /// Inline application and transport continuations instead of dispatching to the threadpool. + /// + /// + /// This will run application code on the IO thread which is why this is unsafe. + /// It is recommended to set the DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS environment variable to '1' when using this setting to also inline the completions + /// at the runtime layer as well. + /// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed. + /// Test to make sure this setting helps performance. + /// + public bool UnsafePreferInlineScheduling { get; set; } + internal Func> MemoryPoolFactory { get; set; } = System.Buffers.SlabMemoryPoolFactory.Create; } } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/HPackHeaderWriterBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/HPackHeaderWriterBenchmark.cs new file mode 100644 index 000000000000..fcca5b58aa1b --- /dev/null +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/HPackHeaderWriterBenchmark.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Net.Http.HPack; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class HPackHeaderWriterBenchmark + { + private Http2HeadersEnumerator _http2HeadersEnumerator; + private HPackEncoder _hpackEncoder; + private HttpResponseHeaders _knownResponseHeaders; + private HttpResponseHeaders _unknownResponseHeaders; + private byte[] _buffer; + + [GlobalSetup] + public void GlobalSetup() + { + _http2HeadersEnumerator = new Http2HeadersEnumerator(); + _hpackEncoder = new HPackEncoder(); + _buffer = new byte[1024 * 1024]; + + _knownResponseHeaders = new HttpResponseHeaders + { + HeaderServer = "Kestrel", + HeaderContentType = "application/json", + HeaderDate = "Date!", + HeaderContentLength = "0", + HeaderAcceptRanges = "Ranges!", + HeaderTransferEncoding = "Encoding!", + HeaderVia = "Via!", + HeaderVary = "Vary!", + HeaderWWWAuthenticate = "Authenticate!", + HeaderLastModified = "Modified!", + HeaderExpires = "Expires!", + HeaderAge = "Age!" + }; + + _unknownResponseHeaders = new HttpResponseHeaders(); + for (var i = 0; i < 10; i++) + { + _unknownResponseHeaders.Append("Unknown" + i, "Value" + i); + } + } + + [Benchmark] + public void BeginEncodeHeaders_KnownHeaders() + { + _http2HeadersEnumerator.Initialize(_knownResponseHeaders); + HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, _http2HeadersEnumerator, _buffer, out _); + } + + [Benchmark] + public void BeginEncodeHeaders_UnknownHeaders() + { + _http2HeadersEnumerator.Initialize(_unknownResponseHeaders); + HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, _http2HeadersEnumerator, _buffer, out _); + } + } +} diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/HttpClientHttp2InteropTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/HttpClientHttp2InteropTests.cs index 5592457e803e..8f8e57bb2662 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/HttpClientHttp2InteropTests.cs +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/HttpClientHttp2InteropTests.cs @@ -25,38 +25,27 @@ namespace Interop.FunctionalTests /// /// This tests interop with System.Net.Http.HttpClient (SocketHttpHandler) using HTTP/2 (H2 and H2C) /// - // Attributes are here to avoid testing http. Remove when https://github.com/dotnet/aspnetcore/issues/24902 is resolved. - [OSSkipCondition(OperatingSystems.MacOSX, SkipReason="https://github.com/dotnet/aspnetcore/issues/24902")] - [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason="https://github.com/dotnet/aspnetcore/issues/24902")] public class HttpClientHttp2InteropTests : LoggedTest { - public HttpClientHttp2InteropTests() - { - // H2C - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - } - public static IEnumerable SupportedSchemes { get { - // Re-add "http" when https://github.com/dotnet/aspnetcore/issues/24902 is resolved. - var list = new List(); + var list = new List() + { + new[] { "http" } + }; + if (Utilities.CurrentPlatformSupportsHTTP2OverTls()) { list.Add(new[] { "https" }); } - else - { - // Here because theory data is checked before class-level attributes are checked. - list.Add(new[] { "Remove when https://github.com/dotnet/aspnetcore/issues/24902 resolved." }); - } return list; } } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task HelloWorld(string scheme) { @@ -77,7 +66,7 @@ public async Task HelloWorld(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task Echo(string scheme) { @@ -107,7 +96,7 @@ public async Task Echo(string scheme) } // Concurrency testing - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task MultiplexGet(string scheme) { @@ -155,7 +144,7 @@ async Task RunRequest(string url) } // Concurrency testing - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task MultiplexEcho(string scheme) { @@ -265,7 +254,7 @@ public static async Task VerifyContent(Stream stream) } } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task BidirectionalStreaming(string scheme) { @@ -323,7 +312,7 @@ public async Task BidirectionalStreaming(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task BidirectionalStreamingMoreClientData(string scheme) { @@ -405,7 +394,7 @@ public async Task BidirectionalStreamingMoreClientData(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ReverseEcho(string scheme) { @@ -522,7 +511,7 @@ internal void Abort() } } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ResponseTrailersWithoutData(string scheme) { @@ -551,7 +540,7 @@ public async Task ResponseTrailersWithoutData(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ResponseTrailersWithData(string scheme) { @@ -587,7 +576,7 @@ public async Task ResponseTrailersWithData(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ServerReset_BeforeResponse_ClientThrows(string scheme) { @@ -611,7 +600,7 @@ public async Task ServerReset_BeforeResponse_ClientThrows(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ServerReset_AfterHeaders_ClientBodyThrows(string scheme) { @@ -640,7 +629,7 @@ public async Task ServerReset_AfterHeaders_ClientBodyThrows(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ServerReset_AfterEndStream_NoError(string scheme) { @@ -667,7 +656,7 @@ public async Task ServerReset_AfterEndStream_NoError(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ServerReset_AfterTrailers_NoError(string scheme) { @@ -699,7 +688,7 @@ public async Task ServerReset_AfterTrailers_NoError(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] [QuarantinedTest] public async Task ServerReset_BeforeRequestBody_ClientBodyThrows(string scheme) @@ -756,7 +745,7 @@ public async Task ServerReset_BeforeRequestBody_ClientBodyThrows(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ServerReset_BeforeRequestBodyEnd_ClientBodyThrows(string scheme) { @@ -815,7 +804,7 @@ public async Task ServerReset_BeforeRequestBodyEnd_ClientBodyThrows(string schem await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ClientReset_BeforeRequestData_ReadThrows(string scheme) { @@ -861,7 +850,7 @@ public async Task ClientReset_BeforeRequestData_ReadThrows(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ClientReset_BeforeRequestDataEnd_ReadThrows(string scheme) { @@ -906,7 +895,7 @@ public async Task ClientReset_BeforeRequestDataEnd_ReadThrows(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ClientReset_BeforeResponse_ResponseSuppressed(string scheme) { @@ -948,7 +937,7 @@ public async Task ClientReset_BeforeResponse_ResponseSuppressed(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ClientReset_BeforeEndStream_WritesSuppressed(string scheme) { @@ -988,7 +977,7 @@ public async Task ClientReset_BeforeEndStream_WritesSuppressed(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ClientReset_BeforeTrailers_TrailersSuppressed(string scheme) { @@ -1029,7 +1018,7 @@ public async Task ClientReset_BeforeTrailers_TrailersSuppressed(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [QuarantinedTest("https://github.com/dotnet/runtime/issues/860")] [MemberData(nameof(SupportedSchemes))] public async Task RequestHeaders_MultipleFrames_Accepted(string scheme) @@ -1082,7 +1071,7 @@ public async Task RequestHeaders_MultipleFrames_Accepted(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ResponseHeaders_MultipleFrames_Accepted(string scheme) { @@ -1131,7 +1120,7 @@ public async Task ResponseHeaders_MultipleFrames_Accepted(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] // Expect this to change when the client implements dynamic request header compression. // Will the client send the first headers before receiving our settings frame? // We'll probably need to ensure the settings changes are ack'd before enforcing them. @@ -1194,7 +1183,7 @@ public async Task Settings_HeaderTableSize_CanBeReduced_Server(string scheme) // Settings_HeaderTableSize_CanBeReduced_Client - The client uses the default 4k HPACK dynamic table size and it cannot be changed. // Nor does Kestrel yet support sending dynamic table updates, so there's nothing to test here. https://github.com/dotnet/aspnetcore/issues/4715 - [ConditionalTheory] + [Theory] [QuarantinedTest] [MemberData(nameof(SupportedSchemes))] public async Task Settings_MaxConcurrentStreamsGet_Server(string scheme) @@ -1256,7 +1245,7 @@ public async Task Settings_MaxConcurrentStreamsGet_Server(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [QuarantinedTest] [MemberData(nameof(SupportedSchemes))] public async Task Settings_MaxConcurrentStreamsPost_Server(string scheme) @@ -1320,7 +1309,7 @@ public async Task Settings_MaxConcurrentStreamsPost_Server(string scheme) // Settings_MaxConcurrentStreams_Client - Neither client or server support Push, nothing to test in this direction. - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task Settings_MaxFrameSize_Larger_Server(string scheme) { @@ -1353,7 +1342,7 @@ public async Task Settings_MaxFrameSize_Larger_Server(string scheme) // Settings_MaxFrameSize_Larger_Client - Not configurable - [ConditionalTheory] + [Theory] [QuarantinedTest("https://github.com/dotnet/runtime/issues/860")] [MemberData(nameof(SupportedSchemes))] public async Task Settings_MaxHeaderListSize_Server(string scheme) @@ -1386,7 +1375,7 @@ public async Task Settings_MaxHeaderListSize_Server(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task Settings_MaxHeaderListSize_Client(string scheme) { @@ -1420,7 +1409,7 @@ public async Task Settings_MaxHeaderListSize_Client(string scheme) // Settings_InitialWindowSize_Lower_Server - Kestrel does not support lowering the InitialStreamWindowSize below the spec default 64kb. // Settings_InitialWindowSize_Lower_Client - Not configurable. - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task Settings_InitialWindowSize_Server(string scheme) { @@ -1462,7 +1451,7 @@ public async Task Settings_InitialWindowSize_Server(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task Settings_InitialWindowSize_Client(string scheme) { @@ -1498,7 +1487,7 @@ public async Task Settings_InitialWindowSize_Client(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task ConnectionWindowSize_Server(string scheme) { @@ -1557,7 +1546,7 @@ public async Task ConnectionWindowSize_Server(string scheme) // The spec default connection window is 64kb - 1 but the client default is 64Mb (not configurable). // The client restricts streams to 64kb - 1 so we would need to issue 64 * 1024 requests to stress the connection window limit. - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task UnicodeRequestHost(string scheme) { @@ -1584,7 +1573,7 @@ public async Task UnicodeRequestHost(string scheme) await host.StopAsync().DefaultTimeout(); } - [ConditionalTheory] + [Theory] [MemberData(nameof(SupportedSchemes))] public async Task UrlEncoding(string scheme) { @@ -1612,6 +1601,7 @@ private static HttpClient CreateClient() handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; var client = new HttpClient(handler); client.DefaultRequestVersion = HttpVersion.Version20; + client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact; return client; } @@ -1620,6 +1610,7 @@ private static HttpRequestMessage CreateRequestMessage(HttpMethod method, string return new HttpRequestMessage(method, url) { Version = HttpVersion.Version20, + VersionPolicy = HttpVersionPolicy.RequestVersionExact, Content = content, }; } diff --git a/src/Shared/ServerInfrastructure/BufferExtensions.cs b/src/Shared/ServerInfrastructure/BufferExtensions.cs index 0ba5911c0af6..3bbf8e08b4b8 100644 --- a/src/Shared/ServerInfrastructure/BufferExtensions.cs +++ b/src/Shared/ServerInfrastructure/BufferExtensions.cs @@ -26,6 +26,27 @@ public static ReadOnlySpan ToSpan(in this ReadOnlySequence buffer) return buffer.ToArray(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(in this ReadOnlySequence buffer, PipeWriter pipeWriter) + { + if (buffer.IsSingleSegment) + { + pipeWriter.Write(buffer.FirstSpan); + } + else + { + CopyToMultiSegment(buffer, pipeWriter); + } + } + + private static void CopyToMultiSegment(in ReadOnlySequence buffer, PipeWriter pipeWriter) + { + foreach (var item in buffer) + { + pipeWriter.Write(item.Span); + } + } + public static ArraySegment GetArray(this Memory buffer) { return ((ReadOnlyMemory)buffer).GetArray();